From c7c24a30a0e887866ca41c655b5d45e657a4ad0a Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Sun, 18 Jul 2021 15:48:01 +0200 Subject: [PATCH] File dump from old repo --- database/auracle_db_create.sql | 230 ++ database/bookshelf.js | 14 + database/data/ingredient.ods | Bin 0 -> 13380 bytes database/data/school.ods | Bin 0 -> 13601 bytes database/data/spell.ods | Bin 0 -> 56684 bytes database/data/variable.ods | Bin 0 -> 15462 bytes functions.js | 35 + index.js | 41 + models/api-token-model.js | 13 + models/ingredient-model.js | 12 + models/meta-school-model.js | 12 + models/permission-model.js | 13 + models/role-model.js | 12 + models/school-model.js | 16 + models/spell-model.js | 24 + models/user-model.js | 17 + models/variable-model.js | 12 + package-lock.json | 3907 ++++++++++++++++++++++++ package.json | 73 + repositories/ingredient-repository.js | 206 ++ repositories/meta-school-repository.js | 51 + repositories/school-repository.js | 207 ++ repositories/spell-repository.js | 351 +++ repositories/user-repository.js | 562 ++++ repositories/variable-repository.js | 204 ++ routes/auth.js | 28 + routes/index.js | 18 + routes/ingredients.js | 183 ++ routes/meta_schools.js | 65 + routes/middleware/authGuard.js | 23 + routes/schools.js | 182 ++ routes/spells.js | 209 ++ routes/users.js | 178 ++ routes/variables.js | 185 ++ smtp/config.js | 14 + smtp/mails.js | 61 + smtp/templates/template-sign-up.html | 114 + validations/IngredientValidation.js | 11 + validations/MetaSchoolValidation.js | 12 + validations/SchoolValidation.js | 12 + validations/SpellValidation.js | 39 + validations/UserValidation.js | 12 + validations/VariableValidation.js | 10 + 43 files changed, 7368 insertions(+) create mode 100644 database/auracle_db_create.sql create mode 100644 database/bookshelf.js create mode 100644 database/data/ingredient.ods create mode 100644 database/data/school.ods create mode 100644 database/data/spell.ods create mode 100644 database/data/variable.ods create mode 100644 functions.js create mode 100644 index.js create mode 100644 models/api-token-model.js create mode 100644 models/ingredient-model.js create mode 100644 models/meta-school-model.js create mode 100644 models/permission-model.js create mode 100644 models/role-model.js create mode 100644 models/school-model.js create mode 100644 models/spell-model.js create mode 100644 models/user-model.js create mode 100644 models/variable-model.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 repositories/ingredient-repository.js create mode 100644 repositories/meta-school-repository.js create mode 100644 repositories/school-repository.js create mode 100644 repositories/spell-repository.js create mode 100644 repositories/user-repository.js create mode 100644 repositories/variable-repository.js create mode 100644 routes/auth.js create mode 100644 routes/index.js create mode 100644 routes/ingredients.js create mode 100644 routes/meta_schools.js create mode 100644 routes/middleware/authGuard.js create mode 100644 routes/schools.js create mode 100644 routes/spells.js create mode 100644 routes/users.js create mode 100644 routes/variables.js create mode 100644 smtp/config.js create mode 100644 smtp/mails.js create mode 100644 smtp/templates/template-sign-up.html create mode 100644 validations/IngredientValidation.js create mode 100644 validations/MetaSchoolValidation.js create mode 100644 validations/SchoolValidation.js create mode 100644 validations/SpellValidation.js create mode 100644 validations/UserValidation.js create mode 100644 validations/VariableValidation.js diff --git a/database/auracle_db_create.sql b/database/auracle_db_create.sql new file mode 100644 index 0000000..61f5339 --- /dev/null +++ b/database/auracle_db_create.sql @@ -0,0 +1,230 @@ +DROP DATABASE IF EXISTS auracle; +CREATE DATABASE auracle CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; +USE auracle; + +/* =========== PRIMARY TABLES =========== */ + +-- ROLES +CREATE TABLE IF NOT EXISTS `role` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `name` VARCHAR(255) NOT NULL, + `description` VARCHAR(255) NOT NULL, + PRIMARY KEY(`id`) +); + +-- PERMISSIONS +CREATE TABLE IF NOT EXISTS `permission` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `slug` VARCHAR(255) NOT NULL, + PRIMARY KEY(`id`) +); + +-- USERS +CREATE TABLE IF NOT EXISTS `user` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `uuid` VARCHAR(36) NOT NULL UNIQUE, + `name` VARCHAR(255) NOT NULL DEFAULT "Disciple", + `mail` VARCHAR(255) NOT NULL, + `avatar` VARCHAR(255), + `gender` VARCHAR(255), + `register_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `password` VARCHAR(255) NOT NULL, + `role_id` INT UNSIGNED NOT NULL DEFAULT 1, + `verified` BOOLEAN DEFAULT false, + `verification_token` VARCHAR(255), + `banned` BOOLEAN DEFAULT false, + PRIMARY KEY(`id`), + FOREIGN KEY(`role_id`) REFERENCES role(`id`) +); + +-- API_TOKENS +CREATE TABLE IF NOT EXISTS `api_token` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `value` VARCHAR(255) NOT NULL, + `user_uuid` VARCHAR(36) NOT NULL UNIQUE, + PRIMARY KEY(`id`), + FOREIGN KEY(`user_uuid`) REFERENCES user(`uuid`) +); + +-- SPELLS +CREATE TABLE IF NOT EXISTS `spell` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `name` VARCHAR(255) NOT NULL DEFAULT "Nom du sort", + `description` VARCHAR(1000) NOT NULL DEFAULT "Description du sort", + `level` INT UNSIGNED DEFAULT 0, + `charge` INT UNSIGNED DEFAULT 0, + `cost` VARCHAR(255) DEFAULT 0, + `is_ritual` BOOLEAN DEFAULT false, + `published` BOOLEAN DEFAULT false, + `public` BOOLEAN DEFAULT true, + `author_id` INT UNSIGNED NOT NULL DEFAULT 1, + PRIMARY KEY (`id`), + FOREIGN KEY(`author_id`) REFERENCES user(`id`) +); + +-- META SCHOOLS +CREATE TABLE IF NOT EXISTS `meta_school` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école mère", + `description` VARCHAR(255) DEFAULT "Description de l'école mère", + PRIMARY KEY (`id`) +); + +-- SCHOOLS +CREATE TABLE IF NOT EXISTS `school` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `name` VARCHAR(255) NOT NULL DEFAULT "Nom de l'école", + `description` VARCHAR(255) DEFAULT "Description de l'école", + `published` BOOLEAN DEFAULT false, + `meta_school_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`id`), + FOREIGN KEY(`meta_school_id`) REFERENCES meta_school(`id`) +); + +-- COMMON INGREDIENTS +CREATE TABLE IF NOT EXISTS `ingredient` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `name` VARCHAR(255) NOT NULL DEFAULT "Langue de salamandre", + `description` VARCHAR(255) NOT NULL DEFAULT "Une langue de salamandre de feu encore chaude.", + `published` BOOLEAN DEFAULT false, + PRIMARY KEY (`id`) +); + +-- COMMON VARIABLES +CREATE TABLE IF NOT EXISTS `variable` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE, + `description` VARCHAR(255) NOT NULL DEFAULT "Nombre de créatures affectées", + `published` BOOLEAN DEFAULT false, + PRIMARY KEY (`id`) +); + +/* ==== ASSOCIATION TABLES ==== */ + +-- SPELLS' SCHOOLS +-- One spell can have multiple (up to 3) schools +CREATE TABLE IF NOT EXISTS `spell_school` ( + `spell_id` INT UNSIGNED NOT NULL, + `school_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `school_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`school_id`) REFERENCES school(`id`) +); + +-- SPELLS' VARIABLES +-- One spell can have multiple (up to 2) variables of cost +CREATE TABLE IF NOT EXISTS `spell_variable` ( + `spell_id` INT UNSIGNED NOT NULL, + `variable_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `variable_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`variable_id`) REFERENCES variable(`id`) +); + +-- SPELLS' VARIABLES +-- One spell can have multiple ingredients +CREATE TABLE IF NOT EXISTS `spell_ingredient` ( + `spell_id` INT UNSIGNED NOT NULL, + `ingredient_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`spell_id`, `ingredient_id`), + FOREIGN KEY(`spell_id`) REFERENCES spell(`id`), + FOREIGN KEY(`ingredient_id`) REFERENCES ingredient(`id`) +); + +-- ROLES' PERMISSIONS +-- One role can have any number of permissions, or none at all +CREATE TABLE IF NOT EXISTS `role_permission` ( + `role_id` INT UNSIGNED NOT NULL, + `permission_id` INT UNSIGNED NOT NULL, + PRIMARY KEY (`role_id`, `permission_id`), + FOREIGN KEY(`role_id`) REFERENCES role(`id`), + FOREIGN KEY(`permission_id`) REFERENCES permission(`id`) +); + +-- Ajout d`une nouvelle ligne avant l`insert de description +DELIMITER $$ +CREATE TRIGGER `multiLine` BEFORE INSERT ON `spell` FOR EACH ROW +BEGIN + SET NEW.description = replace(NEW.description, "", "\n"); +END$$ +DELIMITER ; + +/* =========== PRIMARY INSERTS =========== */ +SET NAMES utf8; + +-- CSV DATA +LOAD DATA INFILE 'C:/temp/auracle_data/permission.csv' + INTO TABLE `permission` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/role.csv' + INTO TABLE `role` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/role_permission.csv' + INTO TABLE `role_permission` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/user.csv' + INTO TABLE `user` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/api_token.csv' + INTO TABLE `api_token` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/spell.csv' + INTO TABLE `spell` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/meta_school.csv' + INTO TABLE `meta_school` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/school.csv' + INTO TABLE `school` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/ingredient.csv' + INTO TABLE `ingredient` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/variable.csv' + INTO TABLE `variable` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; + +LOAD DATA INFILE 'C:/temp/auracle_data/spell_school.csv' + INTO TABLE `spell_school` + FIELDS TERMINATED BY ',' + ENCLOSED BY '"' + LINES TERMINATED BY '\n' + IGNORE 1 ROWS; diff --git a/database/bookshelf.js b/database/bookshelf.js new file mode 100644 index 0000000..6d02f74 --- /dev/null +++ b/database/bookshelf.js @@ -0,0 +1,14 @@ +// Setting up the database connection +const knex = require('knex')({ + client: "mysql", + connection: { + host: process.env.DB_HOST, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + charset: "utf8" + }, +}); +const bookshelf = require('bookshelf')(knex); + +module.exports = { bookshelf }; \ No newline at end of file diff --git a/database/data/ingredient.ods b/database/data/ingredient.ods new file mode 100644 index 0000000000000000000000000000000000000000..06994483c761c412dc5e98548e08b322d6cc0239 GIT binary patch literal 13380 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 rT>$;3=D&N7Uyk{=D7~Nm`CB(FCkggWx�|ynl?}oz4i|ue<*PhVOtx literal 0 HcmV?d00001 diff --git a/database/data/school.ods b/database/data/school.ods new file mode 100644 index 0000000000000000000000000000000000000000..ff4d617e86dbeedd87b0068eb9679e2c0f592401 GIT binary patch literal 13601 zcmbVz1yo$i((Yiv-8E>i;O-LK-Q6Kr7~I_*26uON*Wd)#;O-6~3Gm3h|M_#yedpY@ z-YfPDJ$p@k-CcX{>YA>v6r>>_F#rG<0628#-Fr*)llWn$X)B0xf~`cJ`*WCU(Y7Hm0_Y^gw$DQ$rJ=g{i5d!e207Vg6U} zURxjRY|SjqogDs!24rTix3hP$e}!;j_-8$WzteNDGj}ir0*wqE{);Bg-)R2o?7wUM z-8g}chK^4EAH6x+*;)Vh-u`ZOhQ`LG)~2sUvipCXotd?rp`+=4(|Y?iHnFrZG&cn@ z2wOVZ7}^8>i$VX+G{2^3Z)j_3{XcpzFfjkRonHI-UqN_n*%;bdnwbI}=^aeWCK4uW z0~k>PFL;AYCzk{npr9P6Mq912d)R_Cpct;x(AK-66Vwz7%8s+=cB;f!HliES&kPBM zBkD?eRalLwDMlqM8jonX+v!^iSGiY~nrPpq5nfpZ6lT-3x!BTh=_duzaUfWKsn4Tw zO}pWh?;!1+=lmpr@VOxx29_vF*z#`I6OU_h8`ex+(eWfF*6o3qFJg_mG^Y;Eut}*i zq=`7rPcRBrW;D<-fS^rIdXOdQMv<#G_iTH}pmR1LkJsRmHu}rbdMHBCp5A6>&ACp% zh9}6v{oL5yX?E~xgDlY?gZW(kxShxC<=~nA%M0#yb%V2rwcupF`S;j^hU>Sg;?)Y$ z&@c@T7Bf&_06+i)0Pyc8;P1=__i8?%qnouUklxkCdQD5)Zj~Lwd!w3B#lXUkI&rTd zqI00vRu{UVcgh*(9%BlP0=-bgui&hp+dBk%B>p{DIC^m}0a@5^fB%K&L0^QIw_8H( zbQ+ALoA*|fJh+|6b?6YQJVQsfK=<93j-6 zdtGXmC7J?MPhc)GslvJgH8Mt0UwJw%W1L;NotsY>x$;e8b7E>*3+vQM=F4MHvr1VC zNrCthtXzwPA`WEnFn{t4>|5^nC=>6#r%-b!HOF4O6g!-WZk1VE_Vk2_0U53=x#Hnl zwS7^LPA%HF#2_kZ8`ed=t^Q6h~UUE@F!j*0sLg6 zNXJ%1%nJmKsBN8PqyPmH_$NrPT$#$Fsvw4IwC)r0SZ=|%C#sPznkf`aK70@vV^`us zYrx4UQfu2{$huQt6C>VAXsilT1?iSmBxq@yI@wWc!H}|9j=uNr-<%)Ph@Hf29Av%S z-y2P}*I+{@Koxk0ns5V5Ur@an=0Cw@=y=fhWCyudb^3*>y)vyC;?uaOEtWt01>ErG zifNmnN9qw0nw&Soh@d(WMFmX1AB!3a2{wmFIv7^TwXV0Y2XzqJ4=tP5#Yq`9yvz-% zBF3K36_w*C63t3+rz9!-Qxf(g=xpDyyU7}`)uqp;YjV4mfM?F=>MJM-kku)xE1)z& zIpv6_P{w`GHWU^oW{bvms6=d1jG(8SGjPkPK6rs5r7a!13u0_N@#Q4OPJ&Y;RzVM8 zx-FLA8^<8!L)k#f(41=;uy5<$w<|QB!v+~HT)o4fHmTa`vAQ8@Iv(%N71c@(lE|+O z`BkMYXj(H($7Ls9Xm^T{pvS+Q$@SXc;OW&O=tAj9TkK{rr={0r_qC)HsU(Ngrj~p# zKZ;v}^{#jAXg4-oZ8(`t*-ku2d4%7mUHZ1mOR3i`FXU4GMFhr-b;4^zT^L$X*ltvB zDIT@LSAvlxgo2`11`Qn@KU6XsyD9MmdRA+5iO3QhKU8i)>qAhcU*=)o7}fis#*T9Y z2Zd%R=++Vg9=Ggac2$DJ)9SN*eU9B)E1t5}wvCGmm<1i(kls5dSLe=n5uK3Plge3> z-pk4j6r#>Kqw4#C(GsJ?h6>u9iWs4U!^jVw9Wdh`$Fd6ag@jzSO0?4X-t0x!*SUSL zXbY6kw8wfoFz6}(wi94|vGd)4{#)qKp$C(2LLTyCfbd*?3Y+18!|2jG0G-D<(@avc%TPBM48!~EB znSt9-Yl{l`GS^03N^LZ+9Hhcabb@oWYYp6I=z}YvHo}Sz4!q$q(a4g6#I83*`B_9P znAO8lVJqwhVG^H$>|m&8j}j8D;k~b^rX3N;$-}wT*NoAwk+HZ=QHg>Q%}tjsgzc|x zF?*M|1{TBI_(J15>Q9mE@YtxVRBvyZb|TvV-cXsW57Si#?aaDi6e`z z8dAnUPdUdtopBV})Fn5KD9>W0FIJJ1IB^{j-E}eahl_bHEBZ@?Aun8ltkyfRmX}UY zRyxzBvy*!Lx6OHjKgoQvs&(2&lPfq&XI@O|83B7{MVwc+lRlu~e3M%KZY@$2jxUnD zt#g?w!?E6}7c}$M1#*6n`dEB7hnG)kFajx5meXPg8p0F%ImT060562Hc=l|%rRjc- zo+{uiiqv5Y5Gb(-}I-L3Q%>(C+EU|Imw$L)*xm!%0d77;KC! z@VvKD50OXv98#ITfG^OfVO5m{ z$*@8D^i}%qK<}xA3XbZqR# z7rknqp5J=UW4Ay+tyjw4NwUA`8aLi~FO{b&vhfo8+#}}kJ=MJUJ*;i)?BL->58*oH zk9U}mY3vLudp> z8?P7iOjp)nrv-EU0OP%^DvhQBMyb!>wMrRfspMc*sufRy2^&;gY@k)F0bnZLd3h4m zA;2O*)ix)6m%JBQQqgKU*d$?s^EhY~oQQ~@-_zB#C*Ux>dXc0li`X<&WHxE_yx~30 zBe~$T(aQJPor^QYlap2bud$B=kM?LN0#vg3fbf5BsjT zGqdsq%x8c`EAwfF9&&-1eK_{4{m?0kIHK9tw(8}o9`mZYZ97*>ij9vukF7cx3Ka$5 z8-6&WlPNn*x|9i~o&iyGrGXJSoI1eS&r0|9C~ba^#Et<*+V>5NpMN$`)s}Qa&f#>T z(Ki(_`2<6Hh4iH;WZ}b&k55ztMtF%zat6#TY4`?w5tFiazI$(}j`2wXe*3w8o4@DT zn>$Tj)Nf=A+P9WoK=f!RCc2`Y!ZZT{onPSYb2QrM9ekvR|BUE)_&#}9ASm#M)O4Q{ z0(HD9pxi700&%EoM-#(#D2_Rm_Z_yNgsz&<2h=|QH(>mx9ESc?WczKzzE6Rh$K_w4 zNkZ#Kl6Lr(nMj<6t;u=WD6R^o+F*zwJvFz(#U?vTR0Pg1?&)KLi!kEqpq1B(so1lM zw7wg3NN5<*ETW>Zd&cuKJ=E2CsLk{M9i8aM{gvi@TDv_ilNljB3~%dd7?r(Bo0go1DDSMkq=uZ^UAl2D2&d3q$r(2)4ckx|!o2Y&<;6SFEwZ#3Lx)Dlez?14t?NF&tgd?4}?8T8DmRu5?s6@z*O*07zN>9A5 zXxS}p=G2aj#9;tPO2fHPEiCvm4H-2FF8}eggpo+$%aJT0Cc7ZvWv3!0O#NIzZhfYx zHeCUgZ}9e6{rmGlHdD?+Pd7DAAM3TN`7S3}b*ky@*PMM=xskQI`TG!|cW!DBkNerV zU6>J0`t}<|H|>d<6up)@wo;9`tL$W;16YRU&gLvy&M#%`8|e_ibza^x2#+PvNvVnV zh)AM>s^WvBL$Rb$i(FQWn<=?{i0u1vWR0RlsLZgnfoVNJ`{ehQ*jq%1A#7Xwn+=`y zeG1kaB#p*3zD>4Qh#3KpH~Aqnys`YuFenjBO9ieZX~>VG#%VCt(g8im~Egj)efv#ZaJj6E1w1%%-m65}!(8T7(0u z)8Nh&?MMR*4a$NNqJmB<-Em2s8FpliJ+J}Pb%b%9C*|)dn=MkMjAWdgp`1=ZbWL)^ zG}TGsDmYcje!$Pg?;WiszY*X+d&P)rnke46b~Fw~y={4~W7z}Iy^Fj48DPpZ{SpqQ zqP+v{{nJwjrM^y;T@+rq+}`R&(@yTljHfas&Y)g^BYY5zu!bV==e=09*L4Yd?=kD3 zS?m}EgErO!_klzUErJm+eQ3?7G-6eHTC}3GCVL5PCZa1%9t?O<*+oW0F#d-%=1xQEluJ1W__x{cY+TI zZc}WF97?&p(>ktqyRj$58Y{(g%-J#m-Y*CGPb>U`s=^&@KQ%`Y>k?b=!47)Q{?Vk-wi4lo z_dBV0Gezyw0j}G5LWn_|T9OA6i3N3Lj?i&Fvhq8ty*aYT&B$k;K^#y;(nVIM*XzlT zds8k`c6crygdai8h}&m9!&U)Wk3jju3ZV{DH5f*iU9PTBqJfKc8($UH-YOMa6i|qm z8vcS*>05Th>z**S5J}O*N)y`ab7~&ux|I^ObI*2Iyn*GP1j46_RdY5dPD8I>UzEz- zFsvQ3!*cUuqm)!wHai<#i*UdttVd6in+QQnWIvTjftOG}W)E)*4vDyXw@su_v&uel z70$P6J0?`uM3v*dgEhORmQ#K@_b_@8@y)tv(dlGj(hGpX5jvrXfPm5&-z>Sy#*(gW z_vYNTSvTih9JU5Cy*!o5a`Qm^g&l!Meo_JWxi@ML1}32Y}Y z_l=vW83|%(9dMJ@iy^OClYX+21NXu2op-_#?ONR?BX-T)VCYv&b?2dj(fx=e>!e)Z ziR`qOh7uZE$VAwnA&&Z9Fzv`$x6gLk-p*HdbO+>XLW>EKbNpj8 zuIBaIuWwye#kkfjnxPa`UEFC}Gq0b+J|?BV;!UoVRy(l=HXGf%F~k3Cpvu$_YQ^?a z?tjm8FrS{X`gF>GFQVHD%x|Yd->G)C%_B`zRe#43He%++UA@k`hMdSc0$YyXE9l)y zB}X&HqV7O<`E;{*iDcF3)*54fAJnZ?(8ZfLehY84oHfBcC{xyf=}B=dD#AWC?RK|+ z=uQ5N-E~YBR)O}(m-gHUlB}qs*5n4%C!1HZJkqLe$1nHt#Rx_7d$T_iQ4pQ8K(T$n z@Y%NX+cv0|#~%W99PlGKT)?mDc^sjKX3!wL&eV43-qfPO@pd0r%Qr#L#Z0-F@6M`3 zxY$1@XKZ~jc$jomS9Drh zle3$CasZOy|J zAxcV!D82p%0RUhCnAboM5S25)1pq(*6l7FHUq6DsfrW(xM?^$?1C9=W!2%%Rp@V~= z!@**s!V_U45n`eP&`8j*$Wd@9@CnHgiJ3Uau}O(Z$Y_`tm{^#ZS=cx^dAN8883k$S zg-F;Xso5kc_?6hXr1*p-xTRG^IB4bgX@q&WmH4Sud3eNlB&9@n)P!giKd>3fahfU! zh>D7f$*9VzsHw?_smQ46C`qd*DyXPvYpE*hX{qUG>qx1Z%4r!(YkX9eH_*^B(=@Wu zF?TXo5i?SivQU$>)Q~sP(J<08G&j`wWTfI~s^|1k*TY=Z!&1x4%-qb<+2oVg$4|hI zKo4gtb9)OjcXIXcaB^^S_V9Fb^78Vsw)Hi32rzL9H*<@&atyF_4z+O$ z{OA^8=^5|l8Q|)Z;N$CQ>j&}(2m=Nr0)x_=!ZX|g6I?@5y}}bbA~Rf~^1Nd6y%I{@ z)2n^$tU_F@f;^o--X9aa9sIom{Cz#5eSr}H9?_v*$$@sM!OrP%-nn5eh4EfNL9Y%Y zF)B7DJU%5MA~-!JAvG>MDJdy1A=#3D@bq7i)<}TEhxw@C@9S-ttu(VttcxjD=RCk zXvnW^E^KHiuWcx=Yj3D5YOO1+Z>XG9R6zOQp*%d21JW@i@`mgkn|=9X7h=4X}`SH3RIudJ*NEgz1oUoCvy+1Oc}*gBoq zzFpcrS~?e^s6=H~kT z@%G#8-S?m0@2`LUxP5+p_5)z^zdj?&k|IJXt}AD2J{ssQS6V81!HD0P7 z=WeLeSG{kG_pu)1W)jC|$Igq7LFSatdAfpk4}8cQGLMrFRi3jC$-5lSKkV!T9-p67 zo*b4hl#yUc@t&=3w_I))yN-T-zfcZ-o@}+4_R>{<`CiW7?tAQksPE$0F1t8fDyDqu z^NgR2`GBAiq&lMJbtX^LP!CT;x&_9|2l;wRpG{Syh2XIdN<>7ZR2^Qee}+K^U?id( zm8q4kAiA`ru(7(o^PS3OPFW;jG8bV+?8B*3CgOYdYm|&B*@%WJaD23~H|r)q+DUZ+ z>qFPZvF@(!k{1en5=pIfkVpaISsc!FWu`WDlGRzKN=aV%R^L3*Kut;aXMGF}AM-_Y zaec^wqiwCrvN=Zy0!-az6AgvNZ&Q-OS}%@+D*ZuO(YC6Z9xr~qXMT5>st$5@KPM{M za52PW#RnB@cU*!tkOc^`A^gcdD5ix&`jL|mnS4MP>5^9_w>L+2!F(hR_fV|pqxsiS zW>W6Sup14dTA2*kZ9vev&_{S8n-9zJNl*X{2i6K(;Cju7EqI(quh$KsSfv5P zr3*mqXw6VQ9(pwys46i4IEi7F9SLB~Euw6(F)!QwVM~F}%^7i<*8E2-Whc{e`^Rhk zfkq740KGM|iO9P?P;|QoR**^d;MAf?xE8AFA~||rY0UGGvjJ-QckkoMAvWa&>8Jx{ zTfVKWzE2xwA<%a=?R@X^MyKaYu&a2e0N9+t1Wi}%bJnq!GIow2#;JqT_*Ti7Ka+NO1MVi=}j zA}c#6>unLGet$^3=!;SIF1)u=S|HcQ1Q)h9a5R8tz_0L+v@lJA_A?9KM|+z&NQ@;) zvzLxyL5mE)Kz=dEkC$pYZf;Dn>b;IO6EAaNJd?p&%R3sa>E=fF8NKq71C8aKq#ttt zY1-#Z8SY3uB4Fqa?I}ulYF#Q5c-#64uTDn(s=Ma1jMpa zKR^9p2t!o|RjXd&nPLgmwwFV1n?F6k&j-ivgAONhLRWT`zUf zB!-QjUMBv7`s-u;??Y0l)^v0OR`tltcbx%;K0hX`@OlwOy?=uf+O+qP&|;@&{87eT+Rf%rorqysbuK>FdsyIJ)ZXYY#VUi5#DckL%&Dw>6nL1>cz?mJzW|PSTzo z)(6pmdMOR4g^++k7L*YK(miG0yks6{Z=)kCUK+~;N0)*6EO;?+s3d7?83fRWTy#&l zSu+43CcUD22JJoYZF>eB-|XR8)=|5n6=RgsOwlqZI5w4Dx%HFJA2ZefS%WUsq?3HU znO)Xb22*^A zAXDr$MwnF2FjlPP?@pp0BV5iMjt1Ef%($OO-&5)rmhyE8ao$q$PjtK!%;A;!fBhqIAB-KZ#xurEQpL-F<20tSSGT|=z-;^PoZ;%Rh zozBp~u-njzkElWKxSlj?OQA*l?11c=8jazIeJv?Q850y6-zVNvv z0+mN87Bt+w(586Tk>Xcm zRnL;Vo2)?%WhBRs0wAKs)7I}M&*@Jn=_~mJ{c!#t-`yrqR$D>P%k-rNLGWprIIEGi zm$a%OqJSnFElY8bGBv}1cIv){`e0^){Rd$JzRumbJI_}pH-bvDoRetp!=e*wqY}>; z0qT}Bfu9`spa}mJ=><8~+^zykue`+K~!FTq(cZnS%b$oMK%*(^NE}y9|!l zR99WpWw&vMk`@hqwiYN>9IkNnAk=b2w0AdilMs!Ro}Q%zr{yTu<|_#1(MtdE-PZdu&Qa3t z({K1)4J;p|&(drX;_FMt>yip5zA!os!P6Jd+clngge>JSCA%lQbU5(t$#zs#pbeUx zKzYCJ`ekDlzkem57 zXqFT=yGT(cX{BroO;@yA8ZxS1%Y6`tx(#L@_y+#m!23$M9Fm_R?j@1xApx?#<%4s zK?o7DPr=#pm@kfMm@obMdACdkWJaZ=eoi^hkPcK}nA#Py_0ouVg`G0%xvAN5s-+&S z;_>=xoqd8;x27P+@Ogi~yPsg)b3$OM25dM-<~bSg`F_F^;+O~ctqWp59m@zYrj}Ra zv>4K?2w4~4PUm_``%+Q1`4v@U9+}%0JMK|^c4r+IS=3iG6L$*xmvtPYLkr-NiSe~t zKW=^U)Njf7Pf+tc5zRLBjeuuTgAJ($m|~66nA=KdcL&3c zYr0PmwRY_4dLr1>ICY1oVnIaVPHpjx3dyf~=A<|O6V9X(&sFckZV zLARno)2ye&75ADrn;M=pK_x!2H)(44Pgu-Ba! z$g^mke_9bc&|;qhYP9tq++uM`uyh2gTsM-BffWba4?m|)UncW@qF$Oy-}i>0wbyfZ zNMqmPCKjv7CWv8nmhVb@ExB0`@(R-~z{+?rn+ZxE@VLK@8ZH^4=x?3WFE5eyMMQ@uTRTXnb+T0#nQM?dEWY= zR9#G17nToq;XSbaK-k}RcB%!sLTn`Q606#{sOV~v^O}6b_f`UlvI&$PBdxGR9qJg@ zOPtVPgn#}}&|_d9=&={{?NJ3`3?K{EKW+yNbCnKZ5vuUBplnmUqWgcMSDK182AVT> zn1fxqVT7$mu9wG~4`#UVZzjJPk3^K>mFvUT&t73;mwap8x;QGeH;pI;*|0=)^MiN3 zDO+^JfZ2&9lQ{hp(OUKGlcKX`d#sf;ZSDr25^pBHHkyWvl*Ve$JteOd^7Xi7kwQrd znmVSDMrmxBP#L3sQYnte4MZN5Ua4L{PF_N}g?Dp_2z4IKbC$_u>-jhT0KCArE{H!^ zR;?Gt`H0+PK9^^dpTKjes!!G?&EP(cG~-;U+#b!M+q-Nv!8TRS52COaEdiY_IC=O5 zQmy7#IFKMQ?cgVrtg8>CPjNlDSpt60R`iusM<4qDU@<#SOU6bOUN<@blA>}VRl)}T z|I7yZMWzCpIyzd~n*WOrbf%*VT;WFV66hxFw<#WsjZsc{XTD$nVM)A%6K_ws+Sh^_ zKa|9@yVxD?H=DF62z!2;xzpLBp~a_Ft^Q?HYlU|jdx{yVNaR2*U zJ3S)Z)6T(+0Eds4VM5r&X4_bXYcTZ-j^SjGl;gX!*^;iZF8V=|N*0Vq#mqe1=;N=Y z^?+H~`tLC-cMbJUu*Qpp)CWSHI!TCXc-b0_OgwR^;iA-*LGbM=@m$Cy`s%$ILuFsmY1C*b#%SX zcXsuwLC%doS|^5MJkt$enCdhXR#dJ{iD|q>r8!2EuRq4>gLn0#XCYq)TwEW~A0F?` zKOVUBH`iXsx}@nnwQH3VhQE9Y_K)Frx&m)s_1ZFd(3M&Z4TVh((TGflZZ|a>_<#yt zh>wc`(yvqTw_Ua**^2WDe*Q`h$4CMVX+VZVhIOyG3q*a1Y0vPgd9))DI1}#TMrxf9 zehE^H9wsK{zhK6;^Y9t%a}nW0YQ5ME>N0)$EL2R8E3lFc!O!_Mdk14pWNyq}{*Lo9 zs*}>J60ssxlv-Tu@y$cSX{V4X?t!(l@FtTni*!x~YaYcROq?q|8(V|3TLFA*2t|Dt zthZCaU;wqWR&KZK3cE2RnO57DVPp&e{v2x8KvfX-PrY%PcYzcxOCKhg%d25Y6~)YX z;Cy6cH0Lefa5>@>w^ER~6-w@|y*~(yM8OYAtq&SC-P<(L3j~UBjv=WqS-PKxXvlF# z!!&u7)>DF@eG+ zQs)XGQJOM<`m@fZ4ANodA^-liBnom8OM0AzZ*#&ANp4B9F1jKl5xKXJ99AkbW@en7 z#<5O$V4{A5G8msa2vv#?4WQmvS>EMrOF+(s&-%fkW5EA7RG63Wf%Dq3_ zcitS`FxsOIEms>&zHGuOa4(z>(B-s7HyEv7mR}BbY(0FzH=4u?aa!4)@z?^FC$U%?3vNyJiMc43=&M&@1m_jY&m3_QDTtp>%wYI>FIJbTSH z%hdajIwjbwNxK8NMs^^6+19wk`{?ja6+~qZe*wt~=y9m47ry*SlwX>_rYch&^2zC5;L^Z{6}pAmA)NEN1W#BZ`BeSh^q|a9 zG-UPaWw+-{F7DxVoZ+hm*`5%nd;Q`cj(YX}5;b1U?Kw;AT(CNrQYZD1Yh zz4C@LOUgum>_g-36>Se%+O|7FXwKt%da=&fJzeCK%RrW&03*W z)+kltc(VQ(xuHV{dBm~w@ZALXV=kmYp)QS4x zlBm|?Q8`bI97)Q}=dB&Kk_1T*#^1eNw~O4&6&0!=7@Nq+c$sLd93Cl_-pblo_?f9? zEchE#GQVvOR(~+T$UoluTB)bgePX`G4`*KzV%>D}u%`TeK@4auyOSy#CogxU?RwA7 zof*`0(U!tqL?wchJZ7=pUV4tC$RJkzfK};ZF?2gDYxzFu$vg)MTHLlpI5+0-ex;br z%FV|E(}TbB#`5z#k#COT!^FMEg2ujk^*ckN2Pux1{0FNG_oYtnwMeB_0P8@Uj-2DQ zENi^CSh?){LxYe;;*fvse4XG(9#$3PAUX(BMRh~SO*c1Ehr0$YjS_m`Kw%a4;LJtpH zw0pCl^F4JR4TZ_?v?1v)|4MYw8B3OGLjwQ^!ha+>{*I!H`MM~}imC|FNy>>a{4dq{ zm8nvhAY|K5j3WBL6*Sny+@PsSC5IK~?tcrILqI|!LP1b;_+dmVr=uEYM9uRi_(8yD zz|7@b*U3MnVkp?_(nSrdNl*I}@80WMnZUAb`^qg`rJsm{mdYMck@j*8fucLeI=;kR zB3|nVBIn-b98B{0EIEIsG7VRwo_58JQ~vAH2&{>xSY?T$e$XZX@yeD2*HKh0-7v19 z%1Tw)5A;YUh2(qJR(!_gR&FY8b{XDVCuFb&RW@C4R+*qpYaTo_$~n(*-!Ivd%Aq-q z+~6qa(iNwCc&mGyGqYN}mL#UT6ku6&b_ao`+p|P5E4(B+ZR_EO%uyoP(3Xrp1pP)K z2aT+e3-pla6lWMG)PcxDUtev#(O2I+U*!#|_^s(kn@D}RIXf7L!b_>Di4{(lAYzeWBP=YO5YYn{X&H}M~ue`Y@Z%A5X{=+~s_?-GIk)cZ5X z?iVlexAeclyq5X=izWG=T7ORQ{^H;Lmblkp8~w?kIDYo y@&46J8L!08KmP*yPt8Ajk6(`Yw=gmMgPT^6hI(a`0syG5f2OZar-<>_r~dWW0wSCx_3 zmDTdnz#u3901yBG%4WF=A=aFcbN~PV|6G5o0BkI5Oq@OJO$_Yqtu2fUoGt8Z>0ND& z>Ff-gES%`<>`iQq?TlP(Ol+O$oa`M<42+%3O-!8S{|}je#r)5K^|vH!XKQL<=HmD- zX--V^_ICCz_J2jV(Es20aQ+>iqn(+fiIbC|f#ZMR#P~Oy|0w&q|}PR<6-F8?3e zobBwa|FgD#*E<6vBNJiM4o=5NWyz}CXl#L1b?(b#l4Y1%f30U`L7C&XlC zO@JO0)RAJc!!oa*HB=px{vi`-r#Ci9RiU`@B5&!iR*ZQ!wmIwC0B1a^v3x+8#fXx0 zQo_9DoSLhPuA^j=Yh$gI8b1^F-ZH2pkE+wnmWop^HH4NO#(K7?kitFlkw>nZXkeMc zK>}v9ITivEFGk4X`N$iKbLIfjR87J8GC$tqm4G*Do2w$f5z3%du_vsRAR#~?22y4+ z*f|KNQ&xI}IrUM2bD-e*U`)SfF{qG7|BgC#c5Npdru0~Muc!V-J80KC#oY78$kSzU z*7A&FceDZ#$pQDE3l>@5-uK|&$k?C`j__+8!2(C-=GHTTadEe zX2DJIq9oEMGig0$o}H~Yd+L{_)UqvW zD4rNbEE)De$pg$>TI523mL^r?k#;8wa#C!!A(PD8k;xQV>eR`D80(VY#D+)@g66G` zE$woratYk89kQ9}qzmV!?K4{(U8=29cJ%quX-MW{%Lq)_djz}oMGSQn;JucGMTTs+ z;N(R6S?F*HhDBOKo|ehwS~{qVyRkyPD%hW!6)o@?Y=M)>O-Lixsen`SJu#c#*v7KU z$wXY*G^T}`T%QUdyyec-eI{T0^`%rtgiwVBi^5y)!BAv)#$6x)+-E)O?O(wS^X*(jK(RH;mJqD8V)pbr?_Rr)~1kD{iGIA76RO=2>uvl^io zvJVoXMm-YMNZ*&cs}j%Tz=K!@xAJ_zz{TVsCi7KgA$hX9`HFBN_pzWtS1?y%s^M;d znB@b6_&1@Lu*PJMBN*z(aLiMlZUe(ft^kdPx8W)byO*j}bT~mK_?y$yH4qB%K&gd5 zCr1co2kZb$;=$XXCPHijTU9ZGEab#<_`sCLksQg?tH*we-ZGpIFVAs9wa#)FxIZ2H zkW*CIqp+x%Lvk(#mRP%Ao$0D2FgzHx*(iu{h*4Xuz^oZRUaDx zJ6t^VU^uM;ubEj?&f)bTN`S>|1&Z2gKO*2nPFinQX>4m1AJjq1F)j-E=Q~WKRd>E$ z;)kp3hgB!nGUt)1_U7decQlIHs?pDfr?+blQo3GT*{W7qM0O!}TPcrc#ggvx@MO6$ z3cIP)~3T}J|HOE7>*JHbxCUekZZ6*KZf_dT-G*}0z1}?LT zgfT=7<3{zkS6NfdgjXRA@pEK0KAxbTEK-DS*6ZnpJ|2lo{?T$M^5ZY-J(d1DH}vYp z0~0FrcT_)SR=#wJN0nHzM6b#|55BAH7HtNS#f6LDIF5_4<_RUEx!Uy=IDQ-Rbf;Rs%r?B{V_)Nga-IyQo-YUG9G4is->-qM^7iBt%uf5+dmKyaw&vfe*=J6=$ z8xcu48GJtio~7Q~_{+LxHB7LSNF@=CftUr2OKxAf(?O|7YW;fIm$!3ACw1zE+J8-) zj&7!>bWY*{3=(#hntde=&TIE(`LGn{%Z?TOF<&1&kDUgHTUWFdcp$SfVF!9(bN`;t zl0U<4=ZS%P$Dq~yfP=6a;yNr*2cmmq>VRL8q^1&Ww$2rzB1RqgT-h=|Jigg!S?{5| zb|tNzohoIEwUFAy<0j6B9it0b0%`a4Dee{w>J z900(Z@;~|6e?`3}&IbQ@*_gyB+iV7u&{rR*BV%Z`Q3icRV|0`G$`me@F6;oGd|M2! z;9nBEz1rkSmT32reBa+r@7JIE&r6>4a~dX3cp-p-c;*Jh3)6UC96TSCo)mn?rL`#l zbTMag`8j?-YMw>PT6{LI<+m*CB`T?x4*1}rAdw3 zMY}UOoTEbwAi^l=wFAlV5bWxywNH8|Fm7p)quOs3!--Cd4Bgr6A1x{$@2cMnQ%pR& zw&f`3H(PGkGLiuWAr?SULj=R)TH@_Y*@cEqZjNQ+C3{Aj^729v_=@~QVGIY>W0=tD z5~(SNuv2Dof>i8$8T5Z@osCWoHVJ(r7wH{7dB@r!UWM+}n)RV4zlT=efY|d5l(TBz zsw?4oXKW5h^aWAo{^VJD*AL+Nl}s=i?4UaGxSha5gt=L(jU)=Fl>K#-31lE3v_&wM z9mpyeYK&DCGOJ`e7qrjxyR)4RV3)~vC+*Z#Xx;{%*J9H1NGNlyTG6D48bhU-T%piup+)HU~up0Ql$nC;nG9cd;?FHL$RD zqIdr9BAvahS%kc-I4l&_KcN+@q=bm#-)AZS03ZOwUx@&wX?|P)0Kfq9GRmU=5P$;0 zK>)(QKm&jw0YIQ4Kmej4LL(s}lEE;O zqEgT^b2GDZ^9!(w%J2z`@(4-th$=|4lM3;3DezOv^9zUz@hFS1$cu7n%kXI{@EJ<5 zONmQ~ODM@Gt7}S&YO6@9C@ZO`sw-)zsA_8Hs>*9=s%U9zNvP&hBhY8jepnKB~5psyY~II$5YVTWWfm z%KO=>dE03D+UuBG7#mr;SUFmoIeWTV8@oB1+q*bAxq7*HIy!rMdN?`x_&9rcd+9ku z7&-?UxyD(!1X_6pS$Rb`djBG^Bqt=MWTxgMCuL>jWG7~4X9uSh z2BuX8W)_8J)WqZ!MCDdRW;Z3|6lN7>#}t+)7Zt}BRi>1eCYIM^7MEm|R^?XaXIIsx zS2blfwiToXm83-#WP}uCgjZ(;*JVZ&WyiPV`B#@j7ZoNK6=hbHq?DIsR8(enltnaG zrgb-_S5_AlR=1YabyZY1R5n+awzjoY7qqpNx3xAjbv8A2cXf9+^z`&()r^#N4_5Rp zG2{tdS&X zwIKdAI@*_D4s&8C!%Bn!cs!Hjp3x;B@w6}(-xXwX7+|xJ>$8)0YgSZ9H$dF_sVIW_ywY!?o@RgveYvR@7x^*MM#rh{*TYS95zs}?ss6h2 z+0?e96!5%!s<-KTd~Js+`t=@xj^20QbGhUF9%(=LaY)bKdtrd8u6Ox%6;qavQg)5x^?joSI2kv)wlJ~MVc4g^^&z8{5+=vkME-e zoBB9uHrk`}wxa@G>wA{Ed$N7Q<~hGcb|+p1_j-D59h~ z@J78of~)a2toM1f3#HpZiA4>B!lkrj%~ZWP1-^rAj#H&pm5X@K3G1_K^t*7E-;%T* zcU{X2$yR~vySv#sQ4KwQ9X4nq)b}x_OCC$jaY&2BJnqFxWLl3~BI~jf;^zsJt*s)3PAUOiO_e!FeDF5U*#n zhRUU;k^x;Ed2_xTbDv`74j!yjI*B{wNa|$SYPKzMsdHkzVT(3%xTNn5GDxIUCO8Gy7W<)WC(&L($}zfmLY_x;t*-J7FZxIiL_XG;b~xP^1}InQ>w%5 zAqmH-XRWYK5l5YZ_zz8g7vHSvZU+>%dP?+i|JXTdU-QA=abGId9PW?bJa@cXW{4NfS)t zdlJGodUZaDeq3RH&cZKX`s*Gi5oG)sxcxcMkz^fPVXpUJCpU*Z7!3R6DPkT<{%3p- zEhskLApKKs7;4WPg@*VG-$+Lnf3f}?O;A5*V7AW6TnuxKKf{yXq$tW3_Cyzm1}F36&bDB+dZzy32dc|FJ7HPNGaP#1JaNS z8&D|{G=eDRL!7v!Ch0?3`B1G71czi{Ig*oTy>OEYw;i*BtiZo1-#b zXOFh5!jvnZFG68O|C)gbu?harm^$|-gH^`oR7PWPg)^_ z)uPL>qmKO8b|-x!6|#kuF-YUtS$zj#ywa_7IK-|?FU^(=+DIFN|8uwz=MRBC>;mbBFUs(SZ)HOn8 z3Z>czEzAtvk(d&l@>}&4HdRlFNIhIpl982H3R&@4yUIa`&QQ=4)6MH`EyO1Xx#JI7 zSykJ{Z{ry2&z+$ymJ<>*i=SypXnf4~*|*3oQ~BbXg%j}677(E%M}URXIb{N1QCs4r z+TyE0YQwA(Y^mx!jt=-8O)+v4TP&V@W2)g@qN_S~UeO3*;N;@t@eL;(iUD0EruU%yW)R~ju^ z46(oepxfb<6sp?0!fI9h3A;6~j$APBRmOXt^hJntd5Rdi^!9x$WUE#*kSkH&B{Cid z6}6?HX796*^AU;O$ZQT}VB=M@>m1Eqo5REGr|g z|K77`cKaNs#qZ_^VEk=h;rVDNaW~rGc~CqN58&&idhN@Q#v(;Y?np4uSk06qP#|H6 z(3PA046ZUsE#NtAW&3#hJKF9x8TkWH3QpQ#+6tAWx5Mi;`G8;`K{H#!3qnSEo0rlC zie_KGEKZ0gXq7VjA864{&wj&_=x?#j|7_R`3q06w)@6J8VV* zU&7sQ3`ARnoPb^)0gNf+9LyFPu_4-vNZ8O2t?`A3U`CdGtRRG#dWGEVZkiJE;>_Pd ziG`N684Gp2sLjGeW3-d%JjK@M!?bgP?eX)T1vMc8xrK@osjl^+N)3W|H}a~elyiP5FF{qOu+v8tvVUqKuINJRc&&TER+akl-LLm=A)z?$l=*@mj6nlySo^ zW^{t}v4(SRqQ+^ogEd+4wU_Z_u9LI0QVu0exs1{x@d%kGA&j*?r0u?%t}%|9FjjgI zX8wy!&@K{sh=I``PL0xfwusbx;<*qgTZ3~@a^rL)*CNHJ__$QsIGrIR5qw7DBOM~a zi|^XQbU4^k{Hinm!V#t%^G9@9Mp;vu4ZO4M&`QR$UAn zEb$CmZ{gF`)p$}>3Jz1>kIR)o)CtvSy=w|p#)%EsIx;{o44(H)0(_(f^~0l1+=hiw`>#Sj;1B(Tj2VN z`XidMHamVaZNt2%lTbxXz+UB~6HU#dkQ6Ca?)D~oGMQAzW*z&(2SyzMs8;$yjvn;|Zql5MNC+1?KWtCeOv{WeCDULR-oG7T! zjcPD3peWKp9y4TeO2V7aSv>3Yiz<}0L4hW()%VR;kUMGxYoeM#`cZNbwnE00b`Cav zg22B<0*fd3upnYIjys_6l@~_jcMqQCS<;5s;RDMbWN3B9*{nb`Iz+!!jI#k_NrW-4 z1krW$kv~R-xB^zC($6|X`A)NeHq@aNXR!*5(TlW<^8jOuw+u7j1+(G_5n03S1dSgf zwDCfKu&y;>8WTw_NVT6rNJvc4f!gdFXb~Z&=@h9gwoa~?`_JVh+wlPne4-?})a4o1_uC@*kZ4b(Up9{Us&Bp;^d}Cn^cJDN7U__v-5NW;$!q6%KruxWBxLAbP z{W}$Q5X?Kz(Ue?;xq>3(+R_U`q%t^Oiea9yDgd1nb|B1+YRd9#5urFW3Y69M$b-D>x8L9)tV#EjB1%L!^w&wxKvQTjvk5~fi#PzQRH=6CwEEw35 zwR^0%NV1jh*UQK>q^%yd$(hY08m=n_xibtkaLJrJGFbLmr8heRs-$7ni*YSpYrL=#X$S?2ke~%%^_; z)Sv1uMqm#sW13FvWG5yMgr!$Ad)4U>T|r-MF^Sja$k+R~jk@R_|L8ceNT+C-P|MoQ zpn5ZwnROlJOj%9pma9~=7CGSyd>2eZUvq8;pwb*t6Ra+ElJqVIRjYQpQb(gs*iUHvoH$=A;ma@t$->`U&>-RTx!WCcFm$la13 z?gYX6Q30Yz61J7) zc{b`PbzF!slJBbuBu*IU$HjwAdX8+?ATDNU#5j9 z$K-crF>=Hu!8mNpNd~nDWZ5KT!Z!UvEnU8jqX5f(6m`9zPrB?d-Q;q%;HDUFJ|aB` z3>58~kqAb9$cZXQ96>#i*C)O@HF-COsR{Sr^(+8MQCX2%A^pJrd&=>jjIEQ2v$KV* z*}syG*V;O6h23bq{C$N}n;N+HRk%8fy;eo}u1ipbHa*Y?36x|s5QSgQK7@%^JYG#nUnKf>m;6x~ZmeU1XNrz#)ZVF%YU%1rt`SaY*jN|OXZJwonl{yoF=aNJs)y>v;>Ord?m&f3ofWLz+>n4y+#g=-cX%z0McTT%|D}}?=k}(w7#5#t-~l7eVXL+66Go$dOyEX0I(*R1gG!+7lLY~O zf>3;y9n$B@Lp-|Q6D64CY;n$n2Qb%7^v`gvcid+qbceSWk!uv#4K^u^2k@?wM%sou z_qP&OHsiQMzxE};he`Kt@L8hdN7E1fK0YtY?qSf#7N_i-i%9lVsM+CdNQ4z|)vZwq zvQfi84^4obyvZa+&b3VL!FmX$(LvA>HPf%yvy)R*3O|KplRNK2Ah$ zh7t#6`?iS9;-#VRqZ0Sg#3Wwd7xP_l0Ve^k1agK9Gt;od!CT7OG z7SrA^bZ?)N51C`DmgHK(Yf@phx}QPdU3&onXT)>yVMD}?dx$+Ir)B(fA8tAi+z&QG z_n)Ek1-)(nDaV?jF8|_8i}lWZxBVuUp*%fyk?55KGf_ObQ(%l|rwPGnKxAT$30l%jPfpc@e^1$|*X*0VcQIrD-{jioL#M zyc5+1uBOXI)9LPTd{QS$tIh`?za$K~9XBggALb~dikeC3C*^%{pP4@D&PUm2NP&PI z=XOrJATBiWx>B$?+OxPK4$2>}NDzsnKH(}@Q<^@(N9br=jXMNY>iOcCXmKbAvZXz@xeUovAor+EkM|hd@dJz_YRKH&+=i0mG+!euvB8N!5N7p zScgxr`e3j=JXj&~?uV#^slx6{0m*DP0Hhz8Uw_xHc@ad9$JA_4vG(iU#-S6g!uGRT zeK;re)iGOg#IP?ki-@V;Pt9J={R>*GKlWT~=#yl=O%>p(t`bV#lBn&^-ZYD!>zF(VSTdc8{MMsh&C|JHX-qpcU#yfXb-f z@@ekKi0YQWT`10VfR7S5A5#xca}DifG1->gKR@;J&31uJ3SyOYaJd}w9=pwud@R{} zI8WK9DR^MQ>Mn*LjsWer*|mrvc~yvGFEtcdWuJPf^HHk`G(68TL~{-udQ4q9h*1g# zeXx91QHB2?$^rRq&omi0BsO;e-K7%*b)V&}h&`dIUSY4)%6olw;w{-8($7XZ54dQP2xeWs1Wn{3K0<1_CGT z_6u)go*+q*VfuHsccnrO$Dw$rOtuB`POb$Qlcxt7)Q?Uk7J+$aRi}M}5(a$sF!s0| z3Zaq7ExOd(nGcP&IB%SCz?|z(pI4-;mU$fPMRQZ6BhR!qTJN7KXDV2k)mFv|IR$?j zvexd#fd6pSx1)|1`KuJZXncHyuuSNFiX zZk^((ErJ5afmIO82rsrA`1PdEG6n+*ZfOVS&&vzR*e3Cc2)gNS=J;8+71yQh`w@$ zjI=T}Yp77jq9u3+J^?Jjz#-uwz$iHWF{GB&R!K0VEL< zD_W?@#8hvh-tgd%+gh7|H1-y&DRiYL z6a3iLPktjEWM(Rl{;R<7OC>O4HoY`f_+*|h+hxq#>tS2pKcLrQpII{fO(oKR{3p<% z|AKC0XY2gWZpXh+U-kS-!WK_0?dt0zk}=v=WsQ&_<6~CRDPclYs+`Ad6hWrkzF3du z1cEDWI*7VKlvk{3?YgwNH6^e5h1hA@(%$CUW2OF+@J{-geTW#)kA5xz0+cIJ*NQgr zTcCd*U}tQ5mh<*~`u1k1Kg+Q~1n29e>1E{D3@`4}ouqKWj{nd1^y=wq-q%gk&msH0 zJB|1EJH<;=y!d$szBuLQ`9Klzv&Xp7%jPT-rVk!7>C!18bjJtJGk+X19%|gXx5M@6 z6VJ!)$4qJ5%$S15r$i(EFZ`I*L1-<{5039^GdGJ&@9!&2J|X@AQ5y1DuC%lsvya`8 z<-@h7iW?PszF33Y!Qc`@44RmCK7{$asj{#8F?@R8s|BT2J6qe_dAq5!)3jC644AXZ zgr8_|oIDv`E4}7C)YZx-AAO!AX9pK~%09VY_|dC$eURG;?^$!WD)?2U$cKz_-hX11z!r0 z$y1ZZ-{SPI8hiA;u_t<>7)oW{z{wY-S$qq2y83OWNJ(h4+TaJl~&-Z$VuA;1h-vfunORFBkEfdNd znWI=|vZn{$Fu&Cff$w=!y~X{MED;O@9Zadlx*Z~q-FuYJfKs$XC(TmF)xr@NpKqg4 zcKW^``AYtHyc-e*FtL1v@3lzlx#&d>zgbn*>3a4Z*iR|97_x47Uh?;N-*5CC5^8DF@cA&xbW3R{hlIi0PVaDdGv1qbf9%xAoPOzG*?wM` zNAKB1Nh437?(mTQ&YJdnX8f>tBvea<&YA6{SONRER-B#0k2Z+0=QHKw$9LeD*ZUNM z)_6CP`@s&o+nm*fkQeo&x@G-6{KgDF+}qBEGVo{D_a%-dVwt~2x7_a|qs`gLyZ=2{ z+#XOOFIpIlRwp5RDNODwSyGVb@d`P}$fDao;5`l<-nd9zZ20&uuef`Y`rW)k1%8O4 zS436WA)I%=)+RlsBVc9>&T6RuW;48YTQI=A!iel={fw(jT*z-S#!E+LTKy z%uOr4h>q7Ub}euf?YVLDo%$|}V_O@VWBM1L7qd+fq94vFMfXDGw^QdlMc%VMX`|;u z*LUN03X5X^>Jb9yqS;7sRX_J!|4PRUsM>Ca&v$vpm(xc=@PH8P10{5~-}FBU(ea7- zZrio^3vbjz?`vyZzhZ3c=X=j@*;;Ll9#^_BoL#fqb)D=ke*H0RYk1DW-dxrnYkH>$s-o2DHI@|#o1poE=Y~u9T1DXhr;r3d%eaX&tLkr+DcWP|!w7AyZ z*8eQ{xZP**Pwk#hf4diY+VXb2yj@qZdrZZU6!)oYtM-F z?BeE2BeLIbDoXe%$u{4`DEu%PQXM>UYr%P!;#&`lQ-RLMqAI%aaEH;sQa~?!_;SAN zFsTB_5rp0qql_{)SKMLv zIjjGZUyekK&VChB!UqdWJ@Q^7;MW9)HL>mhko#tBbo+o!77Egq?Pj$7nde^`5X zf(#O(KBNU>F8!JRI*UFV`z&IJ4w_%sPy8O?R0xq; z81&P&NC026j}7L*di7ylCTPpO&vmrPivU<$H@&G*@bWe6UInFRq6#zH!%<-6M3-+Ve;u8(7xxSqf9rs(;fU^RVVx$0rL zyQUYD#D!TPv|mi>HUr3$vnMpG_?G(VW!1 zZOgeu2}@*-AE3|vw0rY^JRSE%tpG1fmG9UC#?es;CIQ-&)clo62U6Ns;Y4g9S2|JQq2KEZUJ!kf4Ikf*F*b|42RCn%`JH-N+1W8D*F z**_ezf_Pnl)>Fm2*@5;SdH+=-D z<%78c?(yOf)hVcZqe0f+#ZZy6@(>gwK)7jMG4yVB*@ z=?l8>1+O2zS^C(tt>a_VSf-=a1_#=^d9x%^w`zD;j2(#z2VcC(bh$rIAY^+=PVWKr zRv1ZY>?Qg3rYv#E*k7AsR82zAwNAwu`4 zXC%Trx-Qny@r)xcdBuX>vFZ!sl|LZY)(r@=PmtS#d(7PKzeo+_u<1M(L5T4s5w~z8 zF7_4woS<8B%0o^Re=DfqH(YAH8;;)j=3`9Ob1kgRDj#cw_sJygeZ7ZMGe$hvWqvBG9xaBDSidAdbz3+VC z-dXY$zrxmhrJcH!O%4%O*FCdBEf*Wqm=veDxfb@e-^}}7xk%ZVq&4|G4E3kiXN50;xC|c@N?}2}~_^usguE63adjr(!uG5d4v+Sd7_ph%41-K%hTuGNzU( zcBsXOqTaGS^5C~p;Nm=P7?`2rZQWNeqoe^Cgh|P@DH%Yms7~|q(&Ya6ZrAtc<>zC$ zZkAo{h$CF1^Ap=0GK*YQPiw}iE7Yy6>9y+*t{1+zE>Xx}%6nI|*RDEa?X7`44{8Ou zh5sX_wa4DOda|c>$=$6yh23zEzX#-$&`k`}8@G1i1oSQEXVR2}JtX_Y9-cRbI58n_ ziX?r1QH)G%2E1Rw<2pzB>?d~t%2rkt^zHM{ciImrkbC}9Q?i%FFfTrxkp-E@R8ZRQ z{e5AK^}T8*?mMx^b>r_e6x$K8deL9YBf=+km#YPB7C|M%tEKp3(nWEH#{)YrrG3O& zr;SZN+IU85`QdDp+GFNaT&m?0U@1*vCs(NLu6>XpRr^}&>4Voy<&UxJ^?RMB;3w{T z?)Ct9&M~EhRv}lm2LY@C=@Mu$G^0Xe1_t;vPcRL0PU`mR<}lFP!UhFqEU3KH!>^!9 z6gki|Ym8KLm+=u2D$P4e#VdE12#DZp(R+F2Ye3CBid#k*#ybe~y@%*KX%X3e@&xC) zSVKmD_g}MfaAa zd&};M(}4^I9*Tj(0mB8u7CNv~z_7-i8QF{4|1OLGFX$LSyWWE;Oax;75JH1wv~%GtwL5R!LyZZ0z#O_W)5YWVkFZS3YkQV#ZQJj`MwMM z)y#*ePU^-nm-9Of?{JJhCEU0R#@#o+H zmgk}k@JQI}jx-ubV47$ltq znDqQY6e z2wv-Br(qXWv^NA2?4F^3swzqDeFJ-r+<{PmCt2_BvJ=!s)64^+JA~_sS3@ugoHHUb z^z!Ti`K>*mz=r|GW)f&baQw=E#~FwYW|A|)FhkxKVvpmj8La41dIc``3Zm}ga)mJI z_nB;hw8+Liq%z4Vr1kCJF=?O07|8TRiIS|;On+dT1gY!-r~YYRhfD9&f{Kae2zj)y z3v%2o>=Aocr0ztXeDej*}ZF-`6q z?09tDrhc|gW-1+OF0WPhcG-vrxD|}b#zJ%U@^$g~aNg*461${_Ee+T}ClO{c{1hPvH*mmb5TVKuEq-EsO($yZCumeU ztSdK6b4ZACBTbB~zHPL-}NhS_)r&o1_N4|^0gnyV`Tl|$@TvNA)X&!2!iw3g!RQ5NJ# zlo1)B@3FucMnrsu4Q&lC@!vT|l4B=yzN@F*!(&?Ba$7`tcCMw5-AskuT=+<8)zI+W zjP3k9ZIDsE`BRACp&DH&WOEUQiFT9R-=}Q8W=JZ9Q6!3$VTL4=uh1i~Y;t(Ec>VbmZKIyK>PEbc-6H!DRwomt@ap zmR4J+_Lqcym8hBDhC1h7W#)s{{LX>xu~>N8X~MM@PxKM?iyW17qCnYgK?X*Jw@2i8 zj8QY(lpED?&;)QaI(O|mjbAtW0>64Pn=LL*+$T5B$`bq#u)G?oUeIF1Bc}rmCtzwi z>Ok9dd7xhXuxC1oXAigp7aU1zR0(jz-xKmzh{pb3o0xorlBgzz%t4Ep(CizA%#a@J z6SYS-I|A;uW~iw7j45B>wWw@2Bo1Q=Ya|b79D1&`o>43WbM%fV2ucKe$!Uzq=!a7N zgpeQiLo&1I?8enB-l%-n3p zUfjz5I~DlFZSCXug0Ijfd2yAzq|Vf7zF`PoUqvsczZSy!v>EgTalDNB#-X0`quwSY z#(>i7Gvyw^xl-%?sql^deD=_md@UHQkpyTNlg19q{tP&qT8NZyF5^Q)cTzd8ci87g^TJbkR$oM~i ztD}Rdyi8O!pB(KH2Y?S*b{CXCHoOwj3teU)q!F7QW>kQ2w{LSprr=8qMY;tQqQaMB zVP3Qx3paHDZ}o>nQs2&eA1}Z+WB}WS6>Zg!sa}_~e6O1EJK472b@DE(Ih8fSD}zc1 z-F{nMaJw!o&Hk{)i0;tx$t_QZy5bBLltoG1Jjjwm%ZEdd&Xat^S<_-h#gNgG+Q9qEfCOgE0 ztul0Gl~u~sEazIWw`WH92?idNm%%772502o$c@3an=&}KWWpc2V<-qkM3ySto1X>l zRgCBl;QP*|)GP@+_$*AIG(f+`SRkHNyF8(@p(#Q4&eyZpMK8){BS@rJn0>?b!sZvg zpsr}*O?|2YYC=rN*%4Ct;4z;c`jU)2m)ccQV}KwAr~No@_sjMSL}Y7Jl5!QePmu;& zEz{^53!b5@oxnfKR0=YB(8KOo(f+EaEor^15TNuVOG;lh)aq zL0i47d6+ph;y0Ow=pRhc^d zoWpXW#MB&ZI8~J`-;_$K@{c1Fd5pBE50yn|>6yx}ty^X>* zvBR{&HplW*mRYl+YH?#}`_hColEhUAYr>V;;GX{eos79_!gUV40@knc`=e%wrgsW} zNx^zU{Pl4oOhex1g9;^710^om8Y`vED3PSeZ%T3lDQ*+Yr_-mn>y8jSf+ zqjP>#@Wy^l9ik=!>J6>kFW(ILQK!(DVWx;JB}yCjujA2dF3t|a$Y_o%!=OnO;8RuB z>vktEpOy7wN0`I*x|?8BHjJnyNRM-gnMuGC8=;m!VlVZ3u z)!$1+LnRs5a;tFcx%5dKAw78ggOU5*@WzJ=VLs|JqH&~` z8%CG54E`Mp6McY8HbPsQbNW1m+ynSg}({p*M zO9}_gNA__)5H#BdgQl6MP`3_Gq3+(4`a?FK0ZBmo>qvase>~suXat+L-dWFk^#j(R zib=#ZyW%L#6y+HO2%-;pj~yhM(p$MK;S!!*Je)&YKWx1%5s`s~U)(KkgEe2=ZPBG2 z=F;iSLT}6xy}EVS%hakjRte7FY_KXg3t^#1k!-4ayp58w%b?B;4W7?yad@_IwwwSz zDFHS!P3hKQno_R-TcyD5%QH&e+M>aUjOjGPiLjXnH`=Jg#@TWr{G>$K%)Fyphj~Y> zBK(6D1IjhHb9vVJhz3v4-H?kZTt2d4fgPK6@@%UyTN^c=A$3Z2hVmTCiCOdJaFc!nGc@!Z$rD#f#6 zB}VY{IA%W%LrSI{3Ya1INgWZ&9h9I+y97G~Y7aCv+JMBN(V@`@jR)&@)itwA=+G=+dJ!E>ECKe#r)^x0tgpVbh2d6uq{1JG2Ecmu|GY8ELo*X zgR`!QE-4p#tYiIX77RbH!}Y|*xpFf7q-5G`E_!to79T>Utr zg%W&B`}> zwv}%#NwQU%TvT%IsLC#aT8k{VFP$zsT|R-M4YP{Qo^2JKRb93%Y}$E%EW2pwVddH_ zqn%uzLUh!u$g^ihk>{&8c%R-`86Hg~i&UtaYXv&WIiE!whgt+m6AX54QSqWXRv!MO z9$W3aRue@$pzD`Hn?=cHZsiIxA13fXU5L4Gja`&tk{i28F(x)EiI&ST)p|Xq3#eKV zP~Gi3-t96RwoYM}VOD_Iv!ej>7SuMr7rtfXmpY4woWY3?%*YZTiE*EG;oZg&+z*eMs0U?+2T1-M6S_5J z{xHAvRCl$Twn~Fjv^o{7!Io?FE80_&v=uGu6k2xgbxJu?sAuZXf_4JuE?WwXoExp( zI_l)VR{1xZi@qH-p{w$5nf^~Ciz*%08$f}l=Cdj05|&KpIwsW41XsD_nDCUCU{-wE zx1;#<%^lU9+H*Kx;s6_o%P@B^d^iwTR=^-XalcQp06iHO6u`rD9?)Ylp%jZ!cSdf* z8M!MuayW4~F)M`a+ffMnDwASt*OB>h8*N`V&qUcT1HDBlEO0`KNp9CGB*0_>@0%3q ze5SBnr|#fMe!o;wTOkj?M$rx;WZ6ihu%Xa1f!l>N<%=HM)dz(vn9oMSr^(9G@f4BFk@ER+P9;&br~~`Gv=Ied~tknd~tkfoG)gzxP3cn zaaZ`_0JFjl}7ZE^REcN|44(|(A*m+Uh zRvO;7qcnV(N)AgKu+;82E0)R@NUV@s-J5^ zC|x@!IVe5;bh&}qTny|n7T|eo9!1`|Y=(qqDb|msTwRpzj>u0a6H&G*j_wpZ>0p~ohoK`)y1+-bg`@oKZ_y7Di5#YD< zbqrrpX2p=c^-~#Ra@As0i~5smOnQf+MM+e!0`7a^{6i{fEetDO>a3|N-BG_tnqsl8 zZ}`xKZ7q4k`sgf+UgObJ$T*60w@vR=kSTh4{7}G{5?D*K`QK8{53t^zs__>CZILZ5 z+0S|)p}5{3QgRno(a&eD+Qf!q9%qqm6O|spOr16e54UJHD=Qn=QL6v72=WflnY3le z{v8hl8bKD8EejyR@*18v6V(Me%gB$bT&Clx4?5QtFAP35I+3_I>=6+_a)Q3L`Z#HX ztwjHlV)M|UQ1o~*n&H{K_EN`8V}%D!JjH`1xFCh==CgO1pcx<tfGcn*o&W~Rhl3RD(6yLF+&+YR3R&)S`uR?W zHc^#FCOn(sjtGAHmQE(;zu*o{gkfB^pQ5c4R|=7d#-7X@#aTc{i6Mi{6;+7p+awJ4 zV#?R%%^df1O2rxZI=8)7Ke%|kwMSJhUT+EM9>Gjm@?n2;a5XbJ7}#NSU{Sd3tH>M5 z`M(E5vde$aT>=!QjfL#Kg{O}jOc0nije{6tTGX7k>WNT>*of71;0SD$Fks9yz`Ovw z_ew^=eCE$*@Lzl!x=sV0(E^ls48ss%7Op3+y+X9(2GQqw=cj1<=)-Gmc_M&)WrkfL z6SDLcQ++&%*B7lV{}R$T0lJH!V6YB1sF9!y37H81Km0@CdjJD$;R~jlEGC8?z$0pj z!2Hx>Q|?!eZS^zIKDb$VXs{iVC93tUNU7Tk*}K| zGT;f1RzJn${-uX|VY_(TFjB9~bmz)BxH_adq&`Wc9xi@q=4COc!^@(#_t*UQapngC z0JE^YtD|ms|AF1oc`MNlb6BWMYsjQra@i~48L|nDy*No;jHN%=y`&Fmir zb=W@~M#`UrW>-H4a=Bg6m+8+gE0#|5tUC{@1kop?!oz@t#Fg z4hI_|Tk0b!7#l6(_>1jNB4ccCKqLM{w7x9k`<422i;Q*f!SJ$qzV+F-Gg>p!VdF78 zA=%{+YjQ40K4FtccV8P+``j$(7O?8TVQ(f>T1&~`F5lD!E#YhwW0x!JP~}jSqspu* z{;H0u_`{;=>9ML;534$kRrac}ONDltnTf#y~uJ=ESGrgkoBmLb+{0gSy}y+9cA@-2rG(( zv^IdHag`V9&`3e}1-fYSAWjl}!B!|OKsS~6{NAs8cs|Ji8Dc8Q6WYJHxcGJ1kw{-o2*FWnQ_doff_lWN z{6e^+kS$NKY)t(rN*B*Xx4(5@eh0+Ctrpp7Z*mzch7Oy(?;~DjCFg$rP*9u+hOsr&PxZ({eZdA%FQ}p_L>H!D=FInnT>YNwQ;r_GM^MO z%?vHB?J%_1fK12C{g}DgKf^X=4jN~sJBBr9W~1GHZJaH~ObeMgI2bd{%sj5`F!OkR z$L4>FFtmkT)H9G`CL$|F_Ok5wi;*|K7-wGir-)}IJDWwOPNXSDvrqnPx|`X@76jK;*CdQiWT*GM<0t+%x7ErBIfG~cs1~v z$4t;7rFy#&b~()PDoeg{dTIhIJWf$Q6`M4kV{1l=PQ#$E6RU>#80U2)WH_uo>Y1|S z!~WPRQ?AYA=-Lja8myuO$@Y+DEU+Zsse*E>n3?EA-nBxu%8~@O>@<%!GJxZG z`(ZoGU6&+hZKEq`EfCF1#`6?&A9&KmXIQis2Of*l+5A6#eR8if(2VaczfH#A~0kj6$sw$)b_+Kt~0S?C6^DW#;*?U`{J5 zX3ZTGRG&pG5K4Ec#ei(2IL2}3vm}EbM=2T?m0Zvk(n>6MrBN*UNxQ86?D;Nj#}4#NK-)e&ZjPT$1}tu1)q$5H+q1#a#! z=?cb20ox>C^S`1U1t-@eF)gChWH+CFbdKuUMFiVGGKm5n(cX<`fkrz~CddRPGKV}( zb1SKk&Ht8CzpTXq0q`BtlBnUtsN<8uO7jbg@pbHzN{26pubS{>HWxQ_EX4-<=QBrz z)nP3eQ2P()m5kq#F(B<8DS_FGagd{xU8f#2)&TXxGB#9hzFMW7Akb-WhVJeg3UlJ2 z@-z7`m%y!`%iflq&b5W#@hxwdHQ(`VyT%>n(n-@on#}wHZtCz0s8^aS)TU2}Qv32# zs?_c`DoZtdm6}_6>@cWv!${3}E!BlK&XyDLCne%$1?)F<6tLGU;ufm-l&McIFgu^r z@{ZH;TC}`psO3GU7QZ8H+c_+3=A1Ea13z_X^bHUGl9G@iz?mJQFXh&XukwS zeqT?u?f!#VWWKi0&jJ|X$MDZ6l}G~Qg--7YDTta6odTkm=cMypTNTd@{HadUQE{fc z&Gs4?IW6p+I=B6^{u*37F^OUtLQwcPg*r`$E@c{2cz2db=1l_T3l{JT@0P2wE07`+ z40PlO->VesDiDIo8^gtcK!Yptxu~Xrk;j-MlPtxw*9Yc3QL`SrmR<@z5~kyS|I7d4 z3SQ2XqZ=1iXXN$C%c;Cn3*2|->$Yl|lj2&X*sKQkrj8oi`lQ%tajwPJXV<5$#ocCV z)U4EE7gTWMI{L6}C6`$p^GzLf%r%nBp({sM`|_-PdFt@$)x427S$Mf9ii4MZ@ai87 zUS?I`H+58j*AYd*Y5~H)d=>~Shqhu|)iQ7pWf3NF{$Ua=ySL_Xl@epEyiBeD9$)1e zXnsL6gd;{?u#%2fO3Nw9Q|W8&TZqpRg@{j$Kz)@rk#UyL^AkkWh4&l*RrHXiXHLDL zoc}wPI1o0*G8QAD9eG|vSC|l=h9%=Rt>;>PIc}o4uK|C45&hI+2K8^knAd!<7 zd_IU`WLSZyZ%C^FY;+>ARvms1TR}yzrnWFjvsl7@lPv8s^eCXtEiS3<*adzzPK`r` zg~&MA!Dai>v_J0QK3co=58;7`t;>J#A_4EaK7Y&4DU0SIzN01-7?F@}h)6md=s)R+J z{##|v-n2h6h6>){3){Z<4lowXjRVZn0*qO8JzQsnq?drPjxcmYr1K?8%zHD z(EyngUk_C$O<>R4SS{71u18&_WR#o-P2f8C2U-!62Z0xgC`&i;kn4mzYGynC1!)#C za(&LUr?ZZ?Y`Vh}&Ix!_!0FWCwQuF@IOJG}oP${+v@gv(5#Ty26#g}fCMvVVJrNVn zM&c(XqNNy)O73)e+*HH?^C>lMoKbF@4z}k>*~^|bx9jCl7hYwbCg(FhOYp|pVZXSR z66YF5NORV@HcI49No9uHyYrLX-#MT>Eua{#E-&qHNGO3~B~czkl3tZ0u%Dq$EsiC2 zs^e~DvSxDy*PZBWIhH&vmY6v_z;#xL_ktx>!bGPaKX7|8aNCo|B9c2HrEj=B>AUTT zL&_Lh7&(a7D5tDNGC2pmMz* zRItuPyBp|8%WP5vddAerIE%cuI(GB?+dp1-Uq$dLRb`-e+;4f9M3|KmCd707Z)ZblZ|&xl;nwKtRH^Sc_Ov)==E4BiS%HrK5l76DpiKoY$x??I ztSkkdNIwe*ALIxWko}J;)2^`*F*PX&DQQixkcL1vNCl>?T;iiS&1X?g3On{^UqzE39_l^WPq7Y_^*40FD+-aSKB+BW z7nTJv9W$E>C5CAHJ)b#SoqY;+`mSEbY4ZuB#I+4=588Iv9#pmYw8U9w(}mpWbgeqw zc0%oh`Xq$fu$I4VM=k%7P}{52R}WBXd}T7sU1XeA*QeE8R|N00`XtWq4U6F0b`-&X z75M?1a*qw)E5!mkl7shCYnh*iFUzy{E@MiZM_CZ8pX85Ly5x|#i*_?hxb#WvF*P}g zc!)k@az0bk&P5M^(I`Jzf={h%=p^4A&_6al_1s><@#!S^WGDu8s6j2hV3SM)K^&>o z(5Mx|tHhlbT|ocjjf43I{|=+jg{K+dM~__uiwHFanadubOZaHt{^{&b8(lBAH7;AO zU*Y31_tT8a<-!#YQ136b5&ILh_T*T&f>kUrHYuflY4gq1GM3*m$3vD*4Fp1j*l{tS~0E)SjKGr*Eot6^dl0`o~GCl>0d0` zP4j5cwK`-V>+`?K5=FWTwc@Z*8lyucwcX50V@rgRPk1VubGE8ItM%@t-8tL1#o2-< z+rF{KM#JXhAj7rU`N{6o94np{D+~+h+jbPte;?`fGc`D*_=Xk3TA-o@Pmu_HD77d< z90`RTUQ$jSn#XZ8LOyUh2m2;Ky`SKYAyU9TUS)}XI2*lXcmk$-H!p)>@l;7HsWBu) zz?bUoVM_$)m}WsVpGlm7a^Fzd5-I9h8O&#RpACbU|4h#Uyn7%__V~g&PknQOxynOF zF#8C0z{tE|>3rLc()ru@%$NE!$i|~I-O9KU-x*4QXHgX#uuQVNb2AoKjAbm~#iT3( z%2k1~IE}+cjj`fF2X%m5QnPEZUCJWyF4Jdd0lXkQ^@2zEck~pv^^x=024+Y2)vJ8#u$l`?ej0_w#>EV}U%$JRc-I z$rOhFtr!~mq2(561*pn4WAKk^k>Miify~iZ20T(KtAg{w`!b`xH9)#hR}XqDk@F7< z1{1MBD3*#iDjdr=daLe8AaaX;wD8$s$j(_i=|BN|xCox1 zYVmn_*2Ocz?(JaRm91fBnjNa0tk3a|9R`eB3^-~!YFaTRyk(OxyG^jK4h50m&Dbq3`roH&%zjAiSi<}_)Nugk^P5$ z0rS~7(?$FVlc}I&!yjnPz%>bV?grd#d+x^dZTIZDn!8a4@NUdMU(FfJ>@cY2L}`aX zHG5oX<7{pD4q7AO+m0)Dm`g{V3V94Cqf;G#M_l zQBil|PooBBHqMsg&(q?Mq2oZO4#$Cm@y9v7z==>7NCz4Zpou%E%N+Nmt^0s}s zM4Ii5cosEH9po2no__fD#fA3@cU!qp=29yjOg&8JGe8|yE#+>UK1yA#(eoS3YtMyhzW`qa7Q@+rN z@<5^a4F0P#>{ijM8>>229l_z-9{;bLz2^)}*_D?<=v8k?SDhg!9NcTTwShDLcU{ty* z+~)oYEiWFx$z%+%YY^|6_phXiS)}MDSsp6}64%~K)8SUZlbrwOfAx?+RsTX0G z(Vh%>L?A3KqTI)&yJ2w|&r&a8N>8ZN7<)S`t+Ck3x z$k=@V84pgwwxgk=p`)SASaCG0mxhKu9-TUTJZhw&9XM=mBiXZJBl#rg=wf<~j+WE$ z5zLeW@&kcvXiL(m!%-I9&_$(Fhl|SobbMK)&5Wo$xsXiY$O5y=1`6=#&mN+6p5UHiV2+~(Tg z9+<}{IkzC9E}v+@qr#BTjeXWJV@G$9i8C?*gL_YTB#_(yy|R0J2zM7rk(N^+ylTv{ zmd*1#j>hErf_XeFVSaAxT)vtSU%*jmI#MHi^&^YW4K>cA!nxBY>~3ij z4%8n37TdSiX!sejdtI1P@{;_~aJRpxPXY8U^~(7gw(Jvt=r{VGt`6T?(QCHxf{n9f z2~1tUwjr_^PDa-fADCsbIkas?+x3oUYhUiqwj;sKZ41reX1I(!uVWc&-p9*+`RQpb+#Peo)+H>4RE@47~stR#Y-j2 zZDo`<|I4J7zIoOmj^8J71TYbpcJpw`b#|m&Xa7yy4@Y5~r5UnThKiiYn`4}XjJrA< z;|yJIx^}qU%>Q*7`_xj$is5{jc|N9x-hUc$+J{?g7@oFu?QlW5ozJXVZB(ma5PzcLLS9sy=-qJM-*=OH-Zox39vw|L zT=j>ekE74wk8=&Jgt~QD32pA!xXU;F4?#T4s3&YJWvs~YyN;0M{v-RoXb(T-GUY{+ zc%{9?^`=L8ArsMZr(Aqkl%YNN2b76E&x6)GxJItaYG&eM&tsQOmiW{{HX!#cy}c z*5a=R`s#0Cs3l+hhY#x9aJ740&vJK%@0pd;W!3U+7twP1z0hw%BY|EWMgq0#x77kZ zkt`bRuH&7{GhHI-bo{|OZn!BqKj5asEqG5e($_PrOz+iEnZCa^z1HfZJSH)jqL#As zS`;(SlV7-1mnCiRNe~gQrqBndgQ%etkQ0%Q!m50HJvy+rL23gQhIsC(GBFkOqbo1L zE`~+=<1nVkUoOkzOyOZPkKlUPb?Z|8Ql3{z*g>NEw}UfmHdUWWKm6<2;2EtYs<8(e znyfW*OMJJ)w_u5HSZCg=qt1MPHq8H3W=U=K&b6-Yu_&2S$Dd`3ltjkU_?<4h#w?TK z1ppjm?BDSP8gzB_N6^xCtN*Wh{u|O#!!p`BeAw#M(nl($0zDN|RL(th zwGYenT6J}gFI022xR%J%BRR@(>vgwYw}3H*#j?E`ie;}iXT9tq>0e8hB;325&&C0p zNM`5KJreLK!2CSQ4(FINzI6Ij92w0&5Leh(5M~fQ+D*xiv!v4Zlt&Q{#Y*+-z~x@J z#b|>>p`6pzcQ7q*BI}9i0y=3573XhK8 zRNOq=vCP9iQ+2rLS~53o!Q7C98%{>QhFsDbCAp z+wBV6uD}AM7?wTuYbblH6Dij13)00wdI=UDVH}i&OjQ|E6d;)}X4OjX2{2|4J!-Cz zvICp-bK^E8Rf2R6M-an0(0&bdpfwQWcK-L!GT)<@6^A_zkC?30$QSwGDJXlM4<2Wv z_kyE4UeVi6tvRZ$>HBv&@VOikt=Rch;-Ow1Xk9dJLVt37=EanPfy{p{+C8^Cm>`V5 z4h5$3TyG$j+i>im%k4|I<#1b$(*TBH8D_tRGR)e6;fUdg@i>Ulse>4n%#K^jHzdub zJLnp=(Dh666V&G42eIxKu8MTXsx&`bk9CQt%fqi#k$Y|p@7C}q0t&;@%)T9^na^_- z{_kv(oxf3;eF}Td|E|(DL~&7)Zuuav=U0k_0neaYUQ$(8$`hLGu_p*W7hf zH`H@VWas?YuG~*^PjW!w7}f&!?WhHQ$$TawJ6N++PliRaf(1e??MeV2+DS}ho8DEEnsAId}{*go*BSx!q=!F|!|Y(ArE-ax=#5(YaQx+r1koj}jya**$caar10 zm?h^mZwhXv892i2k0kSoSdOq`ocFrC81^%g&;N>OOIW$xq3c>}`BF4Mc%iz`8Jp9| zIDsGecYN%qX9@K-J7P3xa_lxZPYXAO)ysW5s+V7)m3;DE#v!cx=`_iXa7hI{RkoB? zm_@f~_;bvBe&M}h)Gq`!HF1RQ5fzIHJOR_cO(dXzV*`?kdwzL>)gpA^gyi^Oek!IM z8OQIFAXY04r3x$JwX>|2H<`~O;3cCoIJDj@UI6YR2~#qDOFgs;M^5-D59p9BoPS8s zQ;G6+gIGijAB~v>`<_^~>}HO*ept3+!tJK&#hc;Esa?m)=`eV6d^6-*=kjbzy#I0Y z&Dl%uKseVa%PNk|nOT(9Tmgw_9?%rQUvzv8Sg?zYa+u%)@gG0DKcL$_JUqtQwVnuw-ZKrlf{uV)RyThx`Mn#4TUv$SZVm8v&%Xm{`Zb$X`V3Frys!nVRGSp6D*eKxLx2f z5)@*eqF@Hua#II9Q#$KxEbJr2F4bIlW9*0lbLEoAtXBB*uHnMId$RLoyM#Z@h5Z4~ zt_+LS2X+*zKaayxz-TqtH|>M_Ab*&$qMs|CwMAU={UqAO(Hzq!u2_h zzASH9EdD9;8S}9GI~FFC;ViJn#D({q&_rCKrb8bsL@8JLKJ7JhiW^75!F@$Uohop5 zfTw{x(}`j_4|+aJIczX!qcSS}wO~)xIKG-Ym9kTgT{hTbq?eni)7`2WD$>9X2Z3*W zfJ#&{!46Vx-mA@L5xGki3E3>55sxxJVc@;Z)VnOtTmISWmb*AQP!o|a7LgYKr1*(p zL+FpJXYqp(uBed}RXK>t7$oIB~Z;jW|cO^4Z;Zw)8DyHT!fnyl7#*=mh~&d#Yi z@vY$jQD^9ZFsQ==VgFLU=PW{B;&&MaO6suW-yd|hutUR`KFdPByb~;s8L25DHCXXz z>qrruQMuv5ECRTJSSD3FuZ6z}d-PB(-`hlgwj3Ws+Zb-$xY79>N1K2Y^$5aBU;T zGKzAK7UdNcs}o-`g_=%kkKy@L#_;J=6%w>^1DJ*W_pxrUZ-f3fn0so8IP5`yCd1&4 z08felSBCkNSBG2m?4r1T;P27E){gc1D;ZD8iI+X|uO6LhPpAzZ( z90mR}a6)atpxkMS6KWfT+OVehs*alC{e}8#tl{F`83KtXZdk>9RYw(b{d)dr1RWlD7e@-iv5fqiwyL+9&!%*OZXn~Z zKJPjuw_s2^rNWz4qny`KAZN*JIW=#lF!;)_F!-vD!r&S*R4A~hGgPb)@;4x#ih4%q z9B(F5!BNTALZCcC!5#z4&xck(afF_C3?8uFk|J+$ zm$$#89u$ZQter+ry`TupvH+W7%xA6TX4op$wnEq@TUGmEJ1`s_47+?Le|Iud<0w$2 zK>KhMFf4bys$+q%{|Ef5SiO(975cx_awrl>7F3NCbW5Rtp~fZw^SN|d>a^5p>G8Gn za6P534C^Vc?5L;wDh_!dVoMIXD{`7{URmqbWw`#u6?KTMoArJ`ZST=WS5MdkZnIXO zQ#l;xX>nfSb-Zyba+~Jr)jlkdC0}c;hU@$>&(;T^|5OQ!JpH%IguMZtzbMxFGz6aC z3!Y)6%{AWP3x|$Jg^u&X?NA*%PLv&n6`ogiRCt!`uo)hE;iBVkaeVc$QW70594?NI z3&Zl#D?7?d7r1yVyr3f-)K@eXV_rm2+iJ2+QKD4Mw=v;L*Kx)4L*AS>s3q#@$l}Oy zbg~$hz+Tx=0((3Ed&sLPk9Gj&Wm&XJ&d+SJG{*VQ+S;;oCD74=M8+xTJl8$Yu)i6o zzW_*~)3XFRMO=BM>2r6jxl8`%cRG%{9O2EIlvP5zULG@E*ixs|O$Q-|n*LHly*XeNTcsM#8I#$r3 z!L~S-C}913=r}$&KJ3Z|!^ya|qc(Q~AD%oQejiYMF!CW!CoTA}4-~2$l^qoxF%^1_ z3XTf9Qo*ng_}Y#_;O7;&{r=DF{7pRn%bj&t$jij4Tv(w%8mBDaW&bg|fKcsdb~GSZ z>bmx*fG}`CI8h)RuJ-KOu#))Nj!NQ}JocHi#$$6)c7VlPf(1fpAZP~&3R#mukIKg5 zfaivpA)5%U5kcW@se5we0c0lR_Oz&_tFU!F00H~bD|jLSf5A4ADP5GX^iH5tXF14s zROObXt%VaPmZOcZhrP_LghogrNfptt9KoIjz*1fec#(Ym*I4KVmO1X|y4G48oCXLl zG%eDFfrhY2^87nKcAE_w*s{6K**SVT)*Om8hIP)@cGNk)M9)lgehAUZ9v9wroJdu; zQw57Ez=CDQ#j<3MZybDUyV~*r?gIKkFT_YgDLHeVU*~`jt}_Pv7G-x`F;~ImDYJf(;+Xm zXu<48$$Sy%^!W*zLgTtt`mo}~Ghj@Pj{}t33H%!_nTnLds`tWsP7jfhz#A!VoT)s` z(~qMmmlOOKrr58i`O~4X@<7qyfplK{D?Ah5aQ^-47M(Z)7pAem8fjcJS-l-oC|HPfr_N&fyc+#=~`7yfG}C zy|JTk_65T;+Mnh#X<=6ZSY-N~%rjLb0YvxalI%U!3T3chL@AU!8gm)aqC8hol_!L7 zwWyg=7v2k8Zx+!%vIX_;%H=U!uW!6qiuNn+I4v*ltqW?42_NNs>o{WB;S1-AV0RGv zzM%--2#u6RyeUu(uI=6_X>xUgy#R>~+nApnEz>um|=G zW1;7@#6q2zyP2{TdxtBAyD=;zy0N3Kdj5Y;cp2fg4SR}L$fxrI-l8iXP;VHBpMGL; z6T|T#)-=^b!zqh@sX8 z;1vNkEYrTJqfGl?0&b6jA5^;AowN38n^q^@_KSC;sd$#mmJ{%X1l+Lj`=*Y<@52eW zJqlg~U=FP1ox&8>Y5CCywUd@NS#n~@Y&k7&DCA&ROMX*FEqN_kPLXyIQ{{#g3jU(h z-Gwff4IbcgKoNz7dtQLji&c;;lgxX|u=38krS3y1RyleqxzphR7;UEO4e2U{F%9Zh zK{1G_E|lCrg;rhzqhl2qxc37lBwT{_A%KRFkN_8KmRI=hm8~&|8W-L-ni^jFwujI_ za$IhNMzc!TV1BkGg0crSnsnN8)Uc2m2ipO3E=|9Ohg&J_qR`())P+s5VI0_U_Fk=6 z3@8gK(~)PjkaVn9i~|HfSvpT;?UA-3QLJ!tEgH+A;=j$*yDayG`q}G;nCj)v|4uj8 z%*aGl;JfZ{$A&?R!FrCEtHTxW%tKc@(1E}OARS!zxE^$ohSQe2cyP%v9Ya14j8MP)h3 zHaW~z0HE#v5)L;=i~25Ah`Fb@oC>U>4&narggjChIE^!p!2@&~H9wA0=BJ(#VN%Jt zLE*1z{zmgx95RFF#Zw}#QXDMH?N>1Ye*- z26f-TWSRLa!CGGFRMn}fQ`O_E>cQGQbS}+GOW`^zKH%TPArC|?{<8H7Yo2Zo%Trn; zSF<1Ua???vxa;Ze>#O7!MRy*!)r3tV?tQEziTX3-3-Av7LgbBstC!<~T~>8p+YeO^ ze>lsG26FiixsOq$M{@q`w0_5(QkLQ_fd%gsywY#ztf)w|Tzd-rr%G7l>AzLprZ=#j zcu{Oj$QME%UX9S#qKhU>vfN|f9lmhDdQ`wVzdxC|al~qfSZ1}faGe#O@RC@M30Zjy zqIVexF6|8VgjdHFcy;;?UJhOkUJhO#gV)vJ;AK`v4A)ul317g=I(nsx5J*Wd9(gMV zTA;KoJl3L2(LClLDWaf*));31NvnQ zWSFW(yr4vZJmQ@P-8aXZU#x`Y@hZ6Q6zG60WEDJ-z%2>`^3X*IOYa0a+m>T}N2Qfn z+FDrRVh9`{poGjzFGq+aNmjM797(Q#Fy+O-8Oi5=jRiWLml&e!T5GujG(dQvx`q>5 z+sZhR7>noQh8bgctXjvZswFDwtZE%|EIhd~tg~<1QD^^BL}fy8jGeeGY8O{QP!%q! zfJ@OvH8(EQwedlv>3F3SAGap@w+ZYb@+RYpoa=IH(IR%?q~!cyek!Ka(UspPL9ABV z6%{QmHf%ClrQ5kSe3_z`%e~%Y!9`rNik#EaMM4?;ln1l}3g;hEv}LD6wjdUf3y<5% z6X%cXcycJ77*^ibk;!;Co;aWw0;O|#wl%!**n#4 z#WBT@Dc#Gn?#VL6)p@vMFUORldj+=*jRD$r7z2DQ0uW29Y$aEW!WOw&X)G2FHoTgv zRj}o!ShxG=hdTHoC%~B(m6F6tOAnufoE||%i6frf>w?J;)LVIyC1Rwz0Mf64LktTz z*#sK`X1ab8Xh0F5+pqrQ4yBH-_O52bsrzAf42G5SZ96LGpWm_h-y*)2y=2{vVaVuj zwD1<=q9B|av6kYl7u;E-t%KQnYP$sMaF#HC$sXnxtZvtp`DEVfq2w38Wf?8g>1N7f zqqndZiBeuIoY&>&M*TmQkY`;lS}JaD2?1(i|Lc4@Zti zLyn%|j-%&x9H&W+!-XL_hIQi|JL<-tV?7){&t=#)Som>-N-7JJN1sJ`MTg==0fz2R zSfT@diG_JWrFah=geb$-dHjGOGv0DPjfE^?<%#0iC_Z1@l%?493d{JE;eYtR;>%&vQ6t3A?J$LQ~$!~t?pn*1%E|x_elK21lD$| zg&`d7Qg_8CFuS8m5Th*-6t!Cx+jt zrYT}tUC{v9KqkLFq4EkVDdA@7bT>|hrShFRO69wIlj{{igrMNb*uUeMs;6ji^OWsm z1xAJ^sX+83f=k-=@C9Gd3P0y#NuL&B&>Ls8)uB_gg};0SdDmwv_VA@+OC9{${H`4N z>%7BfxVb%$o2mNtKRXQD>2*b~4a>|sb(EPONUtpt?3#tT-8oxp8R?|ja;a{%irbFa za&p~}TpLz#cj~C(K9pSBqt_D|D|GCFe90}(A1IR~h_AJA4WlMJOgqfs!SGqnWy3a^NWhA=pjpskn_6M_xiS+rZi~}YljZ(A@+^dFgvlg zPwd-=6MMrN=1v_o%=?S|>y!m-5{lH8qwlYoNVOP~*=w%Y7!;P;=K|$f#aVUn4S$m$ z=6P{)X(e0ruE4s04gFBvBHXG)xH~*?x_l^IHmpAG)KPt0uP$3D%)Z3@%mA}B6Uyw_ zrp%bzPpzFd2y@Q~^WzX^cRKRKh53M|BZl?8ojU4!_ZQ}`B})?Sy`Im63T;_5f5$WJ>?4AAzbjTOnn~Y&m6brb4P~SPI)cTm)ygQdo=;S{1o`L)He^yo`!l z6DCu^@w8+up2-(Y(Qg+H*w}M zc)+^-LEZKxcQRAsSkTM~de^YBuUkiL-~J!)e`e}^REH~bE!iv864@jR$OS*eyhH7U0RIAo zO#;5$$y4h1tPZOth*wna&cs`SXme510gS0(nb2n z_HMy+`Gldp!dmWju}Y#V{RNv8e)lI4QJPm8qSg> z_QGU8d~_T>96lUA96lZiJ`8J0yLQx+F7WXf`LPB!>9a&)p7R=sxZsCvDf|6NPAK9#ovFE5vRIQneDm-r!L@-M&uLLy_3?FpCpIam;A z3C71^N8r+QGvN{d6>)U|e~m^|9<{8{3(fydhk2I+z@rVgsK$Z4JiuH!HhXBNKm$*h zQZPca)zrNtTOJOS9GWrh=z8%iFwFU1>#}CvffdcQhO|Qs>nBA$ts(809mk4>SYcQ+ z+qI);c9j*U4-4B^(XoLQ?!0PWR&*RI94mHZg<&CY*N#HoHC8xSBwzeKh%xt+ntJIx z)Xl8e2M~B%<_PiF3DI+eaD>>E5QY`ST{|j@pOg4%_4_7e=ikL4tD}vVgT$vHQSAqI zEJ#@D-1ew2F>si0nAi~$hMp#!Iy_Bk_>8|~@0rhJWCa+)jYjpXyc#(Y}mW0SFB&w1{6=W&83d5gchTVi;F)Fv4#z7pV+ETQ*0q#`vZxh%`D2`3;E3VSz z{(^k%$aA_diS|=5rSvX-p9HWiTK3Z@8Un0ZM7O-jd`7kEv{fog0BP%_dnVj#QswO~ zOJ_a!DGz8z6wW`S)DaUizJgdpE?{meO`N-_qsgIYVpyBqv!gb96HOdW3~|!AJnNh+ zPTcln2cS4tRtJ`bMBQq8JjTdwJG9U81<^4|m|@h;nrMsGeb=de4qR z_3t=_EhMA?RBVvycY3I*IKJYX^09cLpQo7d2BX;8-(U}rJq)b?40|_kHlEOZD2D1W5|3;UJ?;(Wb(YJ)$}DN+)lWia35c|4|womST^0Wqip&G+{!}P z4wo`l0~T(|TtyewIGPkvJ(P^HmM|@7o4@vEOYIj-KSg1}fzjTx>Uqk-tD{A&g<&*8 zDUrYp6FL8&;1TO-uukBuIvO#IOq`!kXu@fJLz-_`{@k;p{P`urWiWOy2x3^D+NS-B z^}2Rq@>x__x>G7<*~XH4^I1gA8A6tK%n?+lm$$RTAC$EV#Yy&}eLR;FI&H7?il+t* z`b-~4l8V%u$T&+hfAC~nU3f3xCR5?3BBUk{*?25-FQ)6uv-YT3lrW$9fTbf;B^dT} zs-vaTF0x^m=g>48OjCQ>CzRV`rS`j-Ix-~>c%)@m_}#0c@Vi&b&PYd6xvV8uwsW`* z5`l_)QHCakBfVj&>{|5eC_7fL47VyvfoBT&W)TM7FTCe;y^I95no~<~M9lF>W%z6e zNqDh&vBgsVxej3Pa?|#y4hU|3Ymcl6o8R0cZ;y7Esb;rFcFfk!+oK(Z?S#J~{D$@1 zy*lc->lS{?mERJ{?>Nb~Vo*CtzTp<}yp}zp9kb;mzZuEz8`fs`5BHhhL-MWDd_Ruf zW_h2a;=s!i8Op5X5O(|5-_IVVmvM@nJI!aMooC`J`&a^Mju0J8y`Jbz7 zD8t@xpF;$`-7??Jl#}_#vcxwG<@M_b=It-@FXuC+L@f=aYZv#j0$@H%S%B%Hr2$dO zQZJ&KupsX=%`2Uzy>9}Y?gVPia?3u3k#UU#RV1ut_jy3vsr2zxy6OB=JEq3TaznCg zSoqtoqwu$1%gIY|IKf0cfz}#Kzd<_3jIw>O$(y7uZIw}Cjeu^DR_ZuUsT~~tw5Xw7 zs?ohZqy9J>8^G0}%A4PcBdfBFnXGQ6Y82^R>bRXqFGSj~mbPCGiP(wUb^q>Ud_yY&pGdNUseGZ~Ju=-tMp0uh|e|Y7D*ic~nQtv4&z`p8FkCw79g_ zhtB~Tl2sB(0#x1)lw%^9Hw;LzFFaIyos&{gdBt^m08A-KQL{h39Pgh|5_!iOjc>Ba ziyZ>6hsrnVLgrNdNvXVHscpZGQrrDi{%gsSgnMszn9P4k>+#&*U+4cQ;9Vy_NDLA3 zkVsU~{83&}w0Xh4(BQHvH;G{lPbIS`>y-c|k0Kt5Wo^G5xTA182IXeT3Hs3m-LNXO zUq@Bw0R;WgDEbsN`hGVdSM%AFR`>>r-tL@rov0swP&=*E4RhwtYe|~7W44^IHMJnbq2Z-YXqUp3mJEq3bph|^=wBRVwECX?D=I6w6s;o9}X zL>JV+Vm1w9mE2*Z7CRzs?PpGRnXpMj4#L}#2{Haz7!7y4aV+xIO4Y0VnEWqS+mKfa z`;A6%`441UdB-Y^rt;?|XsDE>S?axJ0xqX#_~sir>&b(%>rbNpR0)eb{kO{d^9Gg= zFN&>&_9D=uM(cu=GEuz47mieqid5%^+Za1kohYgdi$n)?6p7Y|sz<=8_49m?+-&sm zivD#*bgUp%r|(GRNaaZ7NOd$)8P?4X?5LYvkjgq#rHjBt$twk7Y`U z1#P@{S&?a)A7?3H#*VHP1nYJk!79qqpck7RI_((d7-q(>?%^0_SXMl+qpbLL{&ySA zA6`}rmH`thV_X1>A}@)IQy>Y~&1kUYW7%#d;Zk9jN&H(hBCpIftF-*j?{r9fIam%f z(H5SSIJuVx;LA?&9@ft3v&+?9lPxmr^i(z!n4|S6&$lM9U`7Q8h3!yiUpg!}EbNE{ z!%F6X9hJ=MSa4i87#BKLaKRmT?aPIZ$?sF};BcPZkWn4`R%}MK0vI zxC<`q1B1;?j~xvj84Y@l295^1(!j8?dSFLo^>a2UW%m0ZKL47%m-f+MJxIR-fizAF z<7OLq&^cIRm&fnD@u-L}a6~v!A{?%QZ(vxOJ+PxR`%8iH_tSiq+*yZ$5zp>DMgv?; z*uoe!=x@T&U1=&5b>{-LS7lF^veIu;E#EW_I63A~t9ur~KNhqIMU#n6je4J2YC-ic+^C2y*;4hDRSW`(~;Q6lA)Ha zg8NQkGccb~b#@@&FA0N<23?e}^iH5*Upd%!M3gLTEu8);`B$1aBiw~Z@)n8ZNCFPL z*X70V`XrzK6+r^Ki2`qQU283?iUtTTRF|R05$=Q%iD^@O>=bJQI7h!H&YZ*T zGOi4F_g8kTt6t(V-Dfu*1pI>;dfC|7J*g(YC z_p<6Jg2zz$w}}K0@JdSS(m_&-z=bnX^bPh?F(tzB`y`0fimhwm+P!|H)bb|t8TBfo zlQ0kcOeQSyf*4-AhLnuo!nTK(3A6B19?(fwIRB8M;T7ff2C;}-T9d6HaSa*xsOqRDUVXyDB`n7M^-R=0OHIgVgzA=*rWIf z2>+NO4Mk0PR=lK29#$SCZ{H3)#r-%MQCI`OBT}vuK?+@Um3Fy>_dgX0MpMX&*XvSg zc|f6)B6o_z;J8c~aL$W|DrJ7)BIh=1zH>i+;+lWJ4TNDS_LUu_*xyE&N+d?sS>A#$ zzr~DIT;Kl`N80&HjOfpPi2s%BJ%E4`rjQ7*ZVwZE6+!LSkuZl+Ebo`wbMS2e85+JpUZC73|;s6UAIxY4Eyns z;DASGh9&A(HI%5=@Dcwe8jAo{_^GfF2h~UnjdM-DE3p&`WoQ(D-lH7faf$zr#VYYI zxE#tj$ngy;5y8w*WBhVc(PM9VurRg6nKB`9!GRwm+QA}Sfhoyb2jk#`z@ni_w& zqLVAb!t<*d3eRi!gn!3#CG@DE!9H=9@ z!XriN{zVs9Mn`aEa3zdW&tG1W=YPK5pzjNH$8)qOj?Qz2po?u@fX=+p|KHxXEV*%H zS^i4cn3YT#X0j5me3)#ejLnp~*+Z2$q)Mjlog?5tIu$RvN5GO=&9**acCy+uAsg#4 zo371R?FxS~Uo!Vzj~6ll(!q2D97tbCB$0uD!+|{K-s|_AD@mL9D;~SQ)%wOCzkjLg zbu@jK$SBf6Mo$y_rwPAPo;12k3=FmD7Z$YX7D(fH3fP=fnSq7QEW4DUu=1l<$m?3r zBrV3MEw2a!UyX%=da`QqUUN^lzVLu(i=Vgmm3&cGHd9Ze2Q68*2s^=TZ$}cR) z%I$--+i2Y~kg^8cmF?_uF{|z!RdpN^ltWY=Rt_RKX3%55BZ&6-JJqQ&&yn5o_}|9= z-OX0IG?aK>x>3V?juU~Fe~qdNk8Ak9se0eiAXHS4rNulIQu~YO;o%-!w!OK)CZD6QhDw8~! z1n?$KnmZ}vld7YaibTsrj*o@q=Y@r=vL^S5!w8XxT(Ias4kbG5>2(B{{jr26t$kOR zE)C`6mlnF$EPTWN>3x=@io^-IaOTYU4W2+$d_|dnCM)dfY4aIP=)*$^0P*~Wk)(r4 z|Enj_x_78_r^RyzR!;NlpW%o>Iv=QY9m9KOxm|#DXI>zVz>$Y)57m2zYQtXfOAFoN z7NJ^~ZVm^hGe{6hm|kfQqGRR)i%D_JMHCLlTZ}_*^?TKEdq!AlSz;;JY zdS8?Qb~AI|5!q@$#hw@$#;K_W+UCL+{~0 zZ>Qc7j_j6)-;UtdQ2u)9hWvFsmK`GgdH_Bk05-I(IJMzh;mB@z0PYBY4OOR?uBc9P zE~f}%MSO6m`{Uj*x5M$0j^M}xPOe2u(1CtSXJvFs<^<@r z8U!ar7SYKZ9m;7nCs`(k0tft3H#a$6CW^O2o!Cy72a3ny6rV08oX+c1fnJvAwTx$W zyls0nPa~G==YJS>=;AyZ?Fm%6- z`$cKH4D?mt_HlA1EBKu!)(fHzc$~7l2ywKr48~F{Kg>0S0dJJ{&B%&bD$23SMKzbR zazJYg(AKD(>S>sgo{5If=7b`y_@(CRF=(gCPBknBB8ONT>F6HJ*plYpyY9c zpLc185VMHP+T`*t32t54CWG|M;B|fpu%#VGfL3k_v|SV6cc5I6@Q;j8XnmYTE_v0< zkLD1`x&fs0%wLV&?zWUL( z{J6m9kF@2-Nq+V_wdY7?cb-U>hKlJ+Hx$#~r4#J<)PiY9>f%(oqd|Dt?qFqM*}z(h zyGRSoOY7|_Pvzv(UCAlJB7%THVvlNqk6>I=CagI=QzrD|lX@UTO#8t}S4Z1{)4MU3 zhF~J(8EsSJo&=dK&au!&;suQwf@dU3YpZdP5kxKM5ss6ER+VWC3128ZCyAa3KYp7Q zGQq@~0)i-~YAd}%)Bxg2aUJmYJ6g7CzuGTAn!hlONsuS`N;`0+^{b@dr7j&l3D4D@ zt9`cZ1V`;ob#Y~QZgS;RR1ST`CaZ{zwUQM&L`NqdB{Hi<`wG$epA(^oZ)$Xv~Pz-(Lh8X&X63oU3Wo`gHBhQTXgalO;4dF{55>5wmJkdbR z%35J-q$%VUD3mU!IM-AMHNZCsK~ z*|C;Vmnq0vcUN5t##22#lnJYfkdV;4hg*G|r|?~4hEP@`DW`zz3ZRdBHCr6h*JdZ5 zO1MBT>zhIPhCR^z;~`^DI4do9XS-t@LrwRU8=CHat!C5V5f0$58>I7zIeO(yFn4ZageDuuG}b?;l|o6?}RNmX>ADU&InFEwi*cjVYQAoQErJjNABsmYe; z%B1OP$gt@_2EF+2|4|jkcrxd5M&5mSkW}TfvTOwp>+VINXgbr=;7>_w4LC9{m`R2s zapBqYPvo5lNd}e_<4+{hKbSOLy!!O3X@JpVXW>gFFiXsF z>6xSY9(U-!;08c??(p2P@8(@YvxO@gW(yWFj|7nwy~1;v&l6`>#3fc)ghm#vo>Es5 zND-C&we4|zLcb@HXI3*SeyV)Z@6OH6Qkhfd?sD<25EhBWaYMg@D;s_XmO=dZ(7W%^`#|u?K6>{(dVBQV7QGEM-d8p>-nK(< zlmti15BU*B;e&(4@h-&+ZeW#tBp-Mr_ej1Sk`K}G|iJw<4awOpD7ZK|3qKd7J|H*vbXxu8DA?y^EM z^2qFwc}rwA+YGqQiSPIwWIj-6?w#Gi=2p194x4)(n-3kEebjuc*u1m$mBE>r!WXV{ z;yeBdTR}qVnowzyO6MvcXdpBt^Hs8aPbwGL@Fo?vjR8A*uYUw6z1GjQHjzioZ0Y6B zGF<(Nxi6cMk!)JD9%baTJ6SH?E`JTsTrQaujZIus+Qd!MgtjrUqO|oe?{~^W{-Hp= znZ_2ba{}_Omx~#MusU+h)cf!T<8mi({}q=p&v6z;jn^dxli#Pd9-UIVB2Vtr!vV9? zap{1NGl2#PO2i^!Laws|y%XWLW_;Z1O*}!9Beg2dqQ*lY0j1ED=u3QAAOQU^-pL`elS+Tm&N3L|r11wAYZt20Mq{P(?j!at1V{wP`J*C@AxySTvWP56)2v# zp%lf1eV|j%fT;{3Cy=*Mj=M0SUQ7y zQ5as#62x{2(u2wJ-?6?vDg6rI34D5jrJJ#U3pfyhq=|W0o|iaZMh~Qc1+5rJ!)^5l z)>2hk&50Y!<=<#LpAXm%iwS%&#nZr+G+-w6gzKF6g1tJD0@XwpR)l)8Xf@I^yLqp$ zsIzvuSJpbohC2$SLPO!zR zh>HZl*UCVzhf{_AQ~iGJWUr{qYT6^bTH~cfJrt6~%S9woNk#cYi3uh~nv+ zwlEv!z|7bzq1%K<~$&4nYK&a274K4Al@s7>7lZ`3G^E+vQD#ij!KfpR=8_g-eJx?6O`tAlf zx`AL12>1;>(oT=aB+cV}Hvzw4;ytjDd0W8yKVs(zV%H%+zAW+849sy9RWZX*{w}E3 zvm6o9wNoQ@ctSey9xMJlZ(x7(WM8Z{(IjBCXWaLto-L4nXo&p7-VBCL6|N70_&`8(UNH-W>krSA6KtS8A?t`L;Z|e z0_Q75hsGejee_5fWQXa2p;)(PL#%uG;IH_LIAdXslg9yCt+<*EY;wm`vMj5e63Q7P z?OMO~pkz`r$4OAaGIjf(J?UNd`kw&es((s*R?_Xtk>~m9$nz5d`ub)X=fxcL0>oZ% zDso}RymI}=y5}rm{pY%8jQ2Zb5Ae5Gwg>zc;2WxLdp1q`&&9`?IG z`*mZtJnVOLJYc8|?%B`=KOFYogi@rgLH$??wK7ZOvoQbi?8)%@{J1f{%Mq_`0Kx-M z|J)Gu&pqmU)ZY^I4W+<68&cqhqyDvIA_-I`08R_;U{Qb0VqBd1WVqkb_ZEQ?JUKzV z@JfyUx_^cMy^aUkUJ?NNVUbZ+D4zFfIA1-c)prHJKOO?gK=idDPah7_hoLsO zXG0sj1Nt~-{^)!DZ~>|vK}274^8Dc-e;7)Odp4xSyWo#wCXj(A5Ero85lHm)D^DPw zOCaYvBM`&fc3>m7wLu`u#XOa*;Vc@RT_cf+UgpCoWVo3^P@~MIQwDbuiiSc04WM0} zP&qE6p;@vra+z+LTXWK(!=2%`<^+`;lF#Y%|3v>trNwlLf^XQboaIFxeDSVG^RK_f zcZ$ZX%LOcIELnuZKK0MKKAADAto1uCt+{zJb7{zIm!8=?vu(+2hFa|Y?!NQeu*VNv z60ap`2uNndh{GzSm83+*8~L5JB9W~ca`0pVpPnc^^oT6bPlE?qb+Mk%vO*{ttpIoB ztDN#QsK}w>oQe?WkvXTCYa))9i-}-*;Lazw3$6+VL8UK**9geuV`Z?Ws`PyLj?*}+ zUKRbmYa2nd1Ps$SCR35}8Oud!OQnFp-~qe_-i&q*>;+jWk~$thpYn1f-U%6xY<^28 zjgphvBhZ>8m8edH7x4L;tNELuJ8q=Hh4hDtUZl+Q^vN+a$`oot_n#%K$$vOLD3YU74z7nyzMRN&XHosvg*#O@rIs& z2a?Kqki5t%#-tl(r5a{c#(v*>s^UFZ70Yv`Sr&+mOm9&~U%uSEymOOCJ$QwFSy8!y zl?4MOW-^_o$pmgkwp#boKoR4s!wXoEyL+Yk9SIc z6GasviA3b0YSGqhcK5mB-VNG>TP1lb30OHl72YAPTNPor{Gd)6iqS8=>Gj~p8Kg!F z5;6&CrP(_yV3)9L>Cem@Sk!homsuJ1)P@Srk=^pd){VfkZ>Z|;TTu01*%)3e(amgKU8RNuJD8fq=dI4d+?S1r_Yl zBB&?4YVXJ8BIhy|NOVe&0c6hO4050Veu5)hX)fYtqW8wH>u~*V+0aE_#|@vNGr#)% zhwr6FtQKM!O44l{N?r~h9*U=u<&wDpR!!7e+JtY!s0w`Gwt5aJogwH#57!Vx2l4bU z-V~rHLql^tPr9}tq~b}=^N@9oe33!%5dU#aVDR z6pr^T2*)ja$zMU>6sK4LNE6o(IxXfnum=LZvM7S%q^A*%aOPNg=8>xFA?+v;lYW-W zDEBYxEV^8dvph3+vtzoV$kVdwT?iUInvS(KY{>}ql!)c8G*eEd;SIxKdez;kA)*+w za`mnda~r3&+hBZ$lOh~}vCrSDPS0(9n&8ufy)%)an7wa7%x-~+uG0eUOL4VpL{zzx z)09xjrvbGP!IL98Cf4N$!2hz@2Qq^3-Cmz^d^BVXRk|@>en`r`AAIMKJt&+|olXj1 zx}1kMRPd(@`oT06>ICN>g+a1hup~|e@+1<1YM_aT1>v4wyAq&u`?S5q!?)ri-0 z_EWYtMGkd0gAs>eoFHC1hx%Idl5FoEs?T-BwUh?C6jX_)!P;Wl4pf|Tm zkB^NXM~{LZO-l>M0~rtLXEWR_ywGQJ-F=8o?$mK7kh^na85qjm2Nq=S78vq7q=;a~ zqvhg}5#-h03G)8=TqSXDPbwbE0{*YN0d=!rROICbp_Hg&#x-=aw_#@QBv^09xEH33 zd*O3ZpOZSVQwQhtIWW}Y4=m{MEimqDPR$VRoG&)?6S9Pn#8ojpS5s?ROG`}6vm%*u zskF)})OuCZ<3ns=U+5Ngg^h~no|(c2pOnJ=hy@c~+=}Igm{S9L)8MXBle_Pt z?BRzi%N6K${AN=^yg{bwu76)YcK58a%lfgCIiDj8bO1Yb+}ZICx!TZ)V&H}oMT@JC zjI2L(eCoN{bG6f%^_j6a`&eTeCqG2LQ^&;ChSnqlH>^q8Y<*;O{i)MjPuKqF*=f2S znbP&hC)Yl?c4DUvkFE{%!omc2Q77y&=*mvhA*u1ie?BGt@2Brx7hupi4O2l z^a29f{)z%FTtosfOXciQB4KFNxp4@QJqbK4^ zBH~L=#QvOkcSLL|n(BGiEuRj%u~R!D;)~r8v7zhM&<)ouO~hll{F^hbB26!orkGX_ z7tT9ji69OlTNb zmKJmLFqvR8S2IYnA!1;@pPC6Ts_7yNt_!)YiRz^GISFr4p+r4)Mz6HA^yl!FaAM2^ zJMB1z5O`W^oniJSIfuoJF?F~Z!j2<|=Wo-D8N@!MJ z8Gl?glC9k^wz?x2&eY*R6&2OZXVuJQZS#WH;^(Fm&A^$|!+G?icP?S~;5p0RVpw&w zIe5WY1-zJ7?Q`%MBr_ThO1>*al(TBiL3^oBgEtVYwfpMmPP{;;_x~cb5RXs9F;W) zs@kN>|BElE*G8T;Omd9cy5H=1tvq7sDIR@sf}gdgxvV5eo%VCwfl#EyYP8 zzY||pBa=iqp--29?!L+D(@CY6!OA<1D-1_N*q99ABp>W+meRJPAROVX1NcN8*{Oqb z1s@tZunjFZuwkpxL+cah3O?i7;WGnDVbR0aHF!80JkuVZj4A}nMJPozl#iyvrV`WA zOmKP9=2b{w-xj4FYZ|dQhCd-Z$jjWeh|8)0uy+E_4S-IFQ;}Dm27g)^_%~&-h&1E2 z4k{#H{#%MOWoafY)e}-!23``ETUi!+EHKc9&v(DTu;Ihq@1mUCso|A_i`wSnb7_Xt z7JKRGs6|JHCSyYjCSw-p=m$InUQpFpPXZTl7Fig4Zh?_nRqP@cYt+z_GDDSRp|?z8 zfK@|1-5fGZ%qRud;%Es;50VsUme+D&fT4BO z(1LZ9eGc&8uQj%ZkN!O~Ea^S6BxNi7@7utCpP2Z>WbdS8XwWpYV9>OK#DvWmdI|Z| zgxINfW|ZhCNuaAWU|bKCo1W%+b(AUZ-lM zAZ!Erm)&NN5rx=at=}kMdqOfRf_Fh4qw0M;P9wTct^4xLUk6sBvU0BO6A+((?43#s zjY>vt#7oat;MEBpc~+G3D@sKPW&k3GXndvBm}T%YUa|!Jsl_Uoovh&vNJ2ttUS2~M zfxhOXEn7mJ3@>g=o>A&f(>GwNBb0CeAFv}kwIdel?T&>E zT{=bRcte@E|19hmvzzM>D`ITDRhRzuaev zQ^Tf%QyX0eK4+)d5>P&+J60Vcm;#>g>sn?5}7hzq1VxVLXg<}hDRdW zSpZ(_1T&pDh1b{91X=kSSXV zYEEv-2c}%c%f(4KDx3~%SeS-4bP$98%pgla+R?05xw*~I7zUV3F+7OLdBRrD_N(Qr zlI3k}_MTKf&?c&thpX4HxWGvJGbbB9#^8UjJ5V*$w~yS=xBqt*In#1$cGi72E4b59 zH5Ez~QV0CC4LAnwXr@J#BZ$iZMpZ7FTmt)R1;DEUa#=H0<3)sMiSCkc2^Fp2E|K6# zed0dj2;~IdI8a1leJK}tkd7hySi$l4LAG2Z%SDcBR6Ig9ga7mtXy0P{D6rVc@YPB9 zKY*mkk&o~2PUQ_X_aiqn_x}Ws%9&(y=HK!@v(vUae+CK?7vNisQq9gD%RJ4!WP_#cwa^V#A>Oa-*S3o#PC@t|2 z>^{!82*FN9x2^;1qdXGWVfD&TF+OrbG5#iJ%DF=TedJuWAmJHuDzUEAntoQ+=GWnV z(}6_t?wFoch^GP0Y+>!&F?b1_dQzP%p9DN=s$xQ+u%nt@_mdtNdw&TuQWkzh=iQCn zd^6t-j~tST!U9@4#u0J#9bmnrWv}MvmiNUq(EPco$BL?Lz=4t)gY|^F)|j{Efa3m~ zH`1R2L254-xw--VB>m7h@^s+U=ts&(yOR(a4l^3qm}X?-keaL5k>iw4cT!c*t-GmK z9Y&CTMsaUTbt+Rb0gf*# z)rOT%iidayAbx$bl;PRYsg1)UpBeegXzx5^C}lr)L(2Xpg8&|ubqcmGc2&7#@`3<>;9;sNS9-O?ZCi*?_m3}FaOd~M^66jwKTGN8D(@R@1 zqAv-8vsz7O)|n!z>dH#H0X#zEma)*r(VRPf z8P_Ys5s}eyfpo+KPIT^;MZ7J_7Fo@OVODeDvl`#`<-|@M9={su>(AZL*Au@6%Rh&b zDM~3^<5v`+OUUvz)b^_C+v+jXNn=5bZw%QBPoo-*KAjic=g@EPcn29T4}QAt^-E#C zCOlT)-^YbE6;2bn2U0sKPDC81W38ZHr&+-#$XuHh>ntv2cyN>y#Kku#KBo~ z18)meX*c02I%twbMhc-X=V_E`!o?G$Bu&0*O-}X{>@Ed&={Mu#ZaGQ8ey2L8;EUZ+ zu%SW0xf=!rZ;Fh|B%L!SbK5D<8wrB6;)f|JyH+myb0L9^M%2ea2W$7q?^zLyQ_Y%# zrca}EjV8V8N!x9+wA$p;X_`!=?jnJF_cUJq7Ncz^WwpA_NrpPWmDq|1-_8`$RP9DU zD^bCW#WA}(TA&eM{Vbw1UVec0bAQo4z>2vp$F`2}fQ2!MPm4L1`Px>NQnN*y6l2o| z)o5$K9}KYWzu0r^D!I2#9`s>fb^49B89@5IF8}5_GjIK9ESGZ1M~HnN5}-et!6BP?NEXvO6TssEMwJ3~4u_d)jES zwhQKB(mH6tktF~Iz;W7ITJE#nuQO+$mqgY_}9|Qxga*fu3enM?pQV-4L zqyd?xp`J1Y9EFTHt`D>6U^}Fmf*u3%t`I8zp$-xPM#?0DuM*YiXMJslc6950-i~Ef#1=;+Z*s^hsFN4@JJ3)u}>q zH}M&96cafoXb*B@P4&Z{R9eZF^9%mXe;3UmL&Wai-5r;o;KLIkwX??5TD) zR68=H+R?G*=}uCu->D-?Mh#mE1~z&MEKu$DKSS;|NuA)@SHzO7%ummfgwMNFtCmN1 za%C3gjB-NB8s+Tr&mXOpu2}b+oy8kR!>qhKvSSE0!JZWwg@A=tHmll%>X%?5QG&)| zA~h#zTsoJ+-#jzAz>HfR81`)#BRH8#IfCy8uv5pKbncJ|4V{24Y&Zcu{CEAww@k9& zg%eEJZe-E=7QS*Cm~e?5EtG*WO27(LdIPm+FJjHV>*pkCSuB-lG2_d>0GXY%Cwtbe ztShdoCuBcSdQ4Ov4o^_E(H6GSa*ErvxoAD@nnba^>+_F3&-0^WnPaD8pk3T8C;8Iv z)G_g;p`p@+4MQbse0fZSIny3dyX4K`!+5jr0B?5b(%>S#ZN-~IL*5+v!%d%OItL>;{4 zQLat3;lxwDHMBAsLyu~oVEQ< z9gzy=!q8af!h*4m1=fDe6_4J#XIxOHyV&4bbM+=~yQjg+RH|0iik^BrTK*-xfmD^c zb1@Lmu5>Dm$C6hht$52g7${Tm)s(kQ^m<;+`k~M#D0Wdr7;>e^C3r2-qj~L<-so_v z%l=r_O}2G1^K*nl4q&H_J4fCj+Zvh$UASQu^hfqHlkZ*Q+RD*2A@OIGwo+!yXnYNo zhJ!CyoPDj6#HYav6o$?PCY=*Bc8!34k&SV_DXf@62z#Bz1grr>&%=zSioOWr)v&u4Rn0|dq@ zm19Vc+oSF7Ti;_8V0E6&;Zw3%pW*Jh4EImTP6jYeQnKHvV><9PG)lT~!zk%rSQK;N z8YR~$Xk+7`vKy45s(S_O>hxDV4W4P2_50TEoJO38IEzvmqQdiv7t4t%|G9tN*BrMp zKnB)vD`Iq}H%L%_x*qxj)y)KTJ3Xom16wE4EJrZ?0CvhJq@T%th9*cCZkQl_%K+}* z7jDoWqdB9kOiE{pEsH(|{|+}d1;d}q#ayubh9#7!e$=C{d0Bck4n_?j4;4URuqhm8 zxt0S>m|W91RX1H_g{~&|btyPaW+$t4-{IMsd=m$uwI0WjCUmmAZiTJdHl?T71tydd z8TMhhcqr>x*yp4N&dPqLjwmNJ>{cAu=vK7A$}f5T-ogyCom7c&b~td(80VSHb+;7G z(gX^P(Sr~;7D!u{T?o#jNVyUvoe z;vDp+&t!_zGO3QsadIV%C9lp|i5*3f^F&b#(pVz3uDJ7RBD+V9_j`9=c$V}mX`dyX z+$|?r((lwUv816}(xn@2NiUfaj@az3ZRKSFT%~f3qb`B(mJ5kTo4V~5J86K*xXQ3@ z8!;U3QqR7IpM*sgao{TS!e1_?5x+%Vy?L*2j5e`qIZ|605a)ICN=TfZGY=TsoaIa> z^Cd@^-~e{&xHHS$$yP57osurya7uc^rc5q>b&WI|O$z3ZvcT~zOolgX`M-(pP?}N> zMOhJ@ezFIsIXk4sa=O1hMOAbVs_1!R(v`xi%-~`w@}h<`Itj|EZatu8Q%`JfwI?=s z*<9P`R*^;Z?Z50@pMQ1TTXRx+pN6l$`RR0c*0S*D>Rln`{L_5x!r7>!HWQtJy|XqG zPDVFQeuRFfjw#1AG-$fCVbIincqJH8w=_Xi4}c)90lgNtV)-G)JIf3c-g_NV z<*Vx;f@Ct(;8Y~IVPyBjr_u65-qfrAqC2R{L>$xa&(*SQ{1t$2+7`RNXE@nnQQZNr z->I&Fx3@dsHT3tnwBhe#0eJO@WLH5~(()pzGPcD2)pDuRgjK1Jhcypt2eMN;g0=qc zV9n3~<j*e`YW?$IEw&emx7DC{M$02ovrHU=#nk1)=^(uTz z#VlX#LD$YBE6V4L%jLh_X*jq1Iqdltc>QOv=hKQUcIwp7+w9bextSZgwGr+H8%TDG zWqXuvp|qiK#-$D8jNPELyDNDEz@>* zOmTTndtbJC*3x^d`LZJBD>!}O5Kea~m0br^Q`y!|LJK8yLN8JT>AfQ*AcBBM5rt3! zgknMf3BC8CQbbTd5JqM|I!LcU5D=sZN)hQzdJ%}!7vI0;t#N+Wy!DcG*SaUU=R13U z+2`DQlW*_D`7S)g1Fr`o;zpUdUa6V5$BtUW^jlaoJus>TzP;#59pV14{e2XjKRdMUG$V@=8(Y5IFX6rbl zbsq2^CkV)`+IkMZ+vE$v&G4>}w64g@uuGP|t@_)6`v z5+>Mp0b9pHgW;Mv=XDEAhIwGk1=fav*k^J{!UmZ-Eh_x9tq`rx3e5 zr6?C@A*75G2utY6JCORxpNhwm4fHAX4Cs@Jh-8J-y2-FEM>PgeeJ zlJPX+*{zL*#WSMHnJYAX*$s@=tVOitQ^uES?Y{GPeiw(@qC4lOGsqk)+VP`9PtHaY zM4H%}mVga>8uEqSWu&2#+j+QX>HW|hqqGmFD@OC;GJ`pgDmYG!+1e~kL-vBH` zCuBz+C}zi90r+-a%6IaN0UM!o7;QDz_ev_NYX`l;@h_lwQ~aC!Xf0>jPFH0mGfgUA z#I5|5b~wL-WI?_nQ5!lFJH?N-Z*_L+#*YX(d)XrDZ{SQ-u4lV5a z_IUsOl;$M#Lu9S-$uj-f&Ej0;H>~ejbYwV+-tz)4VhWY?hyBViZK!@v?OXONOtLqn zowP{EZG`Ouc&Qy71@igK^GP1T=Q2Us$m*iqi+na?lFkpU966;FcbR9h=FHZJ%j2H5 zz4czo5s0s+TN=sQy)zcUjEYrQQ5}d=?@bv!j%Ssy)9u6-9cdNI3=7I@QqX&elFgB8 zcpCu*ci(lM$FF!^W-z)RH}gvE_2z(4R;;rVn`!;P8;Rqcxzluc=USnbBppx*@+H{k z`)p0i@;m<3N7XHJ#oWHNwt?q5R&BQe|DbI%J={wdOW#bXj?Q;FAgUkG;rAxfEY?)w z(zMnY!mOk>R9bQAsA%1ife6A^coRsa0@rV=D(|_hDQEhum27rl{AD-6hW)ZnJ+c{G zj{DujD@AB$Wjl%*EApgk)5c$9pHZW}gyo0rL7}yfYuK+nE2LVgfSW2ctv&?(Q;ttm zfs?%+K^&(l_HyKp-#dQ4|Fuq(k?v7)dsh;T$w}-i(BWjK?4EE2`MGSpQEC*~O`hk$ zhsXZOK+%!72%P0gEse*Sk-y|sMoD=FeL}oMR66jW!$2+5mWIYbvSy2(o>FAuz;*nKT&u%yi#ciPu zImaAIB3V#Ebgw;mP(X(l1$sRUK|{lhIPly-_qOe)odB+>S>a&fb>YUVwb^?;lXO*V zyA;A3P1Q4)-pDVo&XHE=Qi(cW)VfDmN#^UXm#^QVZ!p@5CqqtP&9Ld7V7~rA%sWi?s#4bM=OWp)KLcFbl>#$bEJnPW< zL$!JWW40nYfOTRVn||FfMz3z`TBW!T6BP&0(pGYZgovhwTB9^CodBJ_{(@4iI;Bs^ zIXd->c(nFlK1m&4J01xf|B*ZcR{>eT=qzR+bYsHxjnPGo$7=}hnFo6UAevGmjpDon z0z$$%W@^n&L~}3$_i>Cg$PMRELd1e9TwQC*TP?DxO|a`tkTjeUqiH@~u2JAsWQ+?& zt1Qjc@cGF-@jBp4^)laCgO;ss<)jI@E3j^QAsV=@h= zD5_aPNyi608Lw))@a)r)tbyOWL943A0pGr;9J3|hNHKA^R?Rw?maMt9>GHFT4Z&fB zr>@U|#{ta;e(Rr%{F^ty?hrWyk%BB@?2g0ri1;w|-+Jg8O1ZSyfn?-ysMFi#9Y5dm5jzit^C*w4s_Z$SICbYnygD9lf8X6m*PTyH9ygYj_kc+s z8oDjYr30`E5h8*({J%xq){lz~uE$}_Eem-*NZX?s%66c-@i!?N;2zD*p(9c|$FoP_ z+y~r=PAJtbpQy^1-pX+H-pVl$Z_h}afQ)W$Bc$HR;2NOxm>a?7Os`>X@(wumfQc-{ zfU*WI``WFEbi6xVCNS&(fFiCq`vNS<3qjjys>d*NbKyxs_qj0XlVJFm`v-ZHf4l&& zgauG^dEabx&y7&m+tC794 z3hGzs*m3hI`s^dC;DbWlnX#XUC0p zPxvfv8%Qul`I5)dgFZ=FV<*}+NQo^kmcEzdhVuOeQKPjYv4?qn#zoZkzw!NA6UQU% zO%-!fGM{9MpNf<*+cRyy$c~Ekp$8OL{f1jGs;N1~8svoU-ctZ~(S?|UX;x6QX~jI< z_VHS(VxG!&yy3>|!}K^=ktmb;-!$5jE@58ajzH6mZzlDP&odqvJJOaFdrJUsKJicX zcyz{UdR=nI5)tJJoU)qB<1eHC)Z>8XZtii;z`M6-|_?aAv_1$rps3 zqa3yydNK~>b7adk>oPQO5Ja``%S~SrJ0EYvdneF?jiP5pe5xIyk$Zd3_vIG=`*$QP zaUorJZT_6gjdg%C_N$v`_8T!YA#D$;b^?d>;H~}3oLpUpKE73ovWy#$FL>!Qj}H2{ z_hM$PVzV3S8ax9_gYDaq&N?|-ahZL*oo}Lbg_4Z9^3a!_q;kE&p8K&a`-VZgsOSXJl`!E{MB*?ta7>k_j=v|1| zYKdr2hK&@nl(pHa%xZ>;ic02OuUqGw5Ko(D0fbni zmZT_iqL~}wh-<6GLw|q0v&74cPY@(7EdE{P_I2puD4NNHA+5-QJPB;}#b|Z4U}JHQ zNhzs5iN@2{*W{|~6H|y-&ns~;MHew<8vaq#4 zu>2#u*lB675bqCVIT-6ImFPForEEngK%wcgZ-amrf!SEOo<%7b%vS2>IIN5WD~5DL zUSaUhrBHV9FGidbPN-WOL{k!4Zg{GrCLzLQ6Lodn-NQM}6Fc7_OyNA6r3+;pWRvUj zn0-^NaTt5Wbp>G{E@?zN>F8^A8b;2QZW?#vnke|KpLOTfl{eu!Zm&(V6QCRuVmBE$ zk$@P>>O2y$Oh~4k%w;$1)rqB8SBjgs4vGA@s(tdg8q9h5Q0ps6FUVjRE+W81FHNI& zjw8+4yw6MumaiL1O1cfx&eW;sf`KBQci$DxNM84_8Re(tM|Pxs5UW5oi8RwTOLu3P z_=;Are4TN`B~<06&PGO?jK*Zu;pe{}g;pCUOpubQm^LVZ!as%ji(=m@SIOmYD%+F6 z^ga(ojUqYwdS(7ub|WzSpR7krr(z;McWb>E|<;;vV? zK7w*ZNl0*G({4_CWvy|`UpFOo8EJbYLRZty^$1>npY&N7xItb z;pfhiy@}Cc&uHwql9sv$e6y%Ds}!S&FL(RdW(DQmvMyqjV3bfx4AEmKQ&D?L=~pYd zSp>6bF@6qxqNHtrxOVI0t_9{3=Zhg97uOZAks9D5q|6EtIRkIG!j9 zMvLVhXKBr^uCo@@o|xIg5*S!!(n<3bzd*B%tiGtJy2g`K^M;Yh$ls_(doP=*oeDUG zP8@RJi=eI3`SbF|I_WxnBv&H?0wSKujf!N#a)Tjv=pK$lOnMOVW?JtCem6iLzZ+>b zVaT7je@QPqWd}S#loii{q$;FLM5s3|LuMxSaT05;7aNBzKHaaibt~KQg#8v{gz-#~ z<9Jzdes4|79ft}7S_RRJl#Ck8A+cSlD($S?F` z$-~vBpV$sEpfVO*-imddHs&NXgte0JoWRjSZ^wKinhKZ5O4f5Plr@Ls zg6jq7KD9TrM2m6ne$7aScBi9YKwTMXc}y1|$Gu~$@&f+K}BjZ{k`y2gAcsUb3RJ#SgW8WZk0k0+->z~Q@fat!o3^9jGQ}~Jsj=bxuoFZyiTrSC3><`5kg39jIo?gArY&L-p2cC8AdIpUlScC z34Cs8nw%s>ygO1@|8a$S(g@6peuwy~mE<^rsA1716nh;&R0ECEs;URsRgbx8S|@Zb z2x4oRGtTJmG56*Ov0f1^fp7bxQMg>YbNK0aL>vxuu1;nq<_x_s5%*w334);#>O4^$ z3YWyHxjRVRG)F}E?L}2@F_@%LJ#p#-EISgH#xjkd%+Fv)G-+z~+vJr;}|}i$;cP@ zehM9LM7KJgq^z~#O;Sxmvh=8>f-{AJoW84B?C@k2vJdPpw|gr4&zzzj$B@*T+e-vd zhn|mJ&=}t!1D{I1;V38v0HTK}0me6gAVvTq000I6%$##>MWdAx!~p=nkB#us-yUwR zZb+=Z2lDT|{(2bOUmE~3LJQ`OadLI`zK_7Vx?{YgB|O|cygl5I7;lMx!r}M{=i#q# z?z=nRM|yeLBkunr63b6WUB5v3Va$I+`Pr}2U!Ztl5m@j4(HdFc|K|tm?vDO%*8I%Q zy|nVA4voNQ2k`oO#+S^0^{n0^umhYcXawQ`2Osf zOdVq@Nd^D}T>$_ne(IS>!1BWSqmf?Xes1W$_iLaD($6G}c!hA(&r{}*07IBCh`;vw zbD~|O@wt#N002L>zXd44Y%>>cH+u}i740Q~{c~5`1LOSnu@$d)n)9f_oN9{ixL9>Oh0-1To3>-)G^Z((=)mz@gKr|;rL(I5z3z+=m-=# bxMQ$L4EFzgCn5duJ{{qSB#gH^{-gCT(?TLf literal 0 HcmV?d00001 diff --git a/database/data/variable.ods b/database/data/variable.ods new file mode 100644 index 0000000000000000000000000000000000000000..e972abe8969d1982733a7c3f6d566513078bdcb2 GIT binary patch literal 15462 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 S0{}pL|1o@bI!7$Op8gMh>&UAB literal 0 HcmV?d00001 diff --git a/functions.js b/functions.js new file mode 100644 index 0000000..929292e --- /dev/null +++ b/functions.js @@ -0,0 +1,35 @@ +const regexInt = RegExp(/^[1-9]\d*$/); +const regexXSS = RegExp(/<[^>]*script/); + +// Check if int for param validation +const paramIntCheck = (req, res, next, input) => { + try { + if (regexInt.test(input)) { + next(); + } else { + throw new Error; + } + } catch (err) { + res.status(err.code).send(JSON.stringify({ + "message": "Le paramètre doit être un entier non-nul.", + "code": 403, + }) + ); + } +}; + +// Check if script injection attempt +const isXSSAttempt = (string) => { + return regexXSS.test(string); +}; + +// Check if object is null +const isEmptyObject = (obj) => { + return (Object.keys(obj).length === 0 && obj.constructor === Object); +}; + +module.exports = { + paramIntCheck, + isXSSAttempt, + isEmptyObject +}; \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..aed8537 --- /dev/null +++ b/index.js @@ -0,0 +1,41 @@ +'use strict'; + +// MODULES +const express = require('express'); +const bodyParser = require('body-parser'); +const helmet = require('helmet'); +const morgan = require('morgan'); +const cors = require('cors'); // module to format the json response +require('dotenv').config(); + +// CONSTANTS +const port = 2814; + +// Import routes +const routes = require('./routes'); + +// Builds app w/ express +let app = express(); +app.use(bodyParser.json({ limit: '10kb' })); +app.use(cors({ + origin: [ + "http://localhost:8080", + ], + credentials: true, +})); +app.use(morgan('dev')); +app.use(helmet()); + +// Server +app.listen(port, () => console.log(`App listening on port ${port}`)); + +// Entry route +app.use('/api/v1/', routes.auth); + +// Routing +app.use('/api/v1/spells', routes.spells); +app.use('/api/v1/schools', routes.schools); +app.use('/api/v1/meta_schools', routes.meta_schools); +app.use('/api/v1/variables', routes.variables); +app.use('/api/v1/ingredients', routes.ingredients); +app.use('/api/v1/users', routes.users); \ No newline at end of file diff --git a/models/api-token-model.js b/models/api-token-model.js new file mode 100644 index 0000000..0c78809 --- /dev/null +++ b/models/api-token-model.js @@ -0,0 +1,13 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./user-model'); + +let APIToken = bookshelf.Model.extend({ + tableName: 'api_token', + hidden: ['id'], + user() { + return this.belongsTo('User', 'user_uuid', 'uuid'); + } +}); + +module.exports = bookshelf.model('APIToken', APIToken); \ No newline at end of file diff --git a/models/ingredient-model.js b/models/ingredient-model.js new file mode 100644 index 0000000..069a793 --- /dev/null +++ b/models/ingredient-model.js @@ -0,0 +1,12 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./spell-model'); + +let Ingredient = bookshelf.Model.extend({ + tableName: 'ingredient', + spells() { + return this.belongsToMany('Spell', 'spell_ingredient'); + } +}); + +module.exports = bookshelf.model('Ingredient', Ingredient); \ No newline at end of file diff --git a/models/meta-school-model.js b/models/meta-school-model.js new file mode 100644 index 0000000..3be356a --- /dev/null +++ b/models/meta-school-model.js @@ -0,0 +1,12 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./school-model'); + +let MetaSchool = bookshelf.Model.extend({ + tableName: 'meta_school', + schools() { + return this.hasMany('School'); + } +}); + +module.exports = bookshelf.model('MetaSchool', MetaSchool); \ No newline at end of file diff --git a/models/permission-model.js b/models/permission-model.js new file mode 100644 index 0000000..857cf18 --- /dev/null +++ b/models/permission-model.js @@ -0,0 +1,13 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./role-model'); + +let Permission = bookshelf.Model.extend({ + tableName: 'permission', + hidden: ['id'], + role() { + return this.belongsToMany('Role', 'role_permission'); + } +}); + +module.exports = bookshelf.model('Permission', Permission); \ No newline at end of file diff --git a/models/role-model.js b/models/role-model.js new file mode 100644 index 0000000..60d2e8a --- /dev/null +++ b/models/role-model.js @@ -0,0 +1,12 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./permission-model'); + +let Role = bookshelf.Model.extend({ + tableName: 'role', + permissions() { + return this.belongsToMany('Permission', 'role_permission'); + } +}); + +module.exports = bookshelf.model('Role', Role); \ No newline at end of file diff --git a/models/school-model.js b/models/school-model.js new file mode 100644 index 0000000..01e142f --- /dev/null +++ b/models/school-model.js @@ -0,0 +1,16 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./spell-model'); +require('./meta-school-model'); + +let School = bookshelf.Model.extend({ + tableName: 'school', + spells() { + return this.belongsToMany('Spell', 'spell_school'); + }, + meta_schools() { + return this.belongsTo('MetaSchool', 'meta_school_id'); + } +}); + +module.exports = bookshelf.model('School', School); \ No newline at end of file diff --git a/models/spell-model.js b/models/spell-model.js new file mode 100644 index 0000000..effa75d --- /dev/null +++ b/models/spell-model.js @@ -0,0 +1,24 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./school-model'); +require('./variable-model'); +require('./ingredient-model'); + +let Spell = bookshelf.Model.extend({ + tableName: 'spell', + hidden: [ 'author_id' ], + author() { + return this.belongsTo( 'User', 'author_id' ); + }, + schools() { + return this.belongsToMany( 'School', 'spell_school' ); + }, + variables() { + return this.belongsToMany( 'Variable', 'spell_variable' ); + }, + ingredients() { + return this.belongsToMany( 'Ingredient', 'spell_ingredient' ); + } +}); + +module.exports = bookshelf.model('Spell', Spell); \ No newline at end of file diff --git a/models/user-model.js b/models/user-model.js new file mode 100644 index 0000000..e68e112 --- /dev/null +++ b/models/user-model.js @@ -0,0 +1,17 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./role-model'); +require('./spell-model'); + +let User = bookshelf.Model.extend({ + tableName: 'user', + hidden: ['password', 'role_id'], + role() { + return this.belongsTo('Role'); + }, + spells() { + return this.hasMany('Spell', 'author_id'); + } +}); + +module.exports = bookshelf.model('User', User); \ No newline at end of file diff --git a/models/variable-model.js b/models/variable-model.js new file mode 100644 index 0000000..61f5316 --- /dev/null +++ b/models/variable-model.js @@ -0,0 +1,12 @@ +const bookshelf = require('../database/bookshelf').bookshelf; + +require('./spell-model'); + +let Variable = bookshelf.Model.extend({ + tableName: 'variable', + spells() { + return this.belongsToMany('Spell', 'spell_variable'); + } +}); + +module.exports = bookshelf.model('Variable', Variable); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..12cf152 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3907 @@ +{ + "name": "auracle-api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/generator": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", + "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", + "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.10", + "@babel/template": "^7.12.7", + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.10", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", + "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", + "dev": true, + "requires": { + "@babel/types": "^7.12.10" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", + "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", + "dev": true, + "requires": { + "@babel/types": "^7.12.11" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", + "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@babel/parser": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", + "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", + "dev": true + }, + "@babel/template": { + "version": "7.12.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", + "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.12.7", + "@babel/types": "^7.12.7" + } + }, + "@babel/traverse": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", + "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.11", + "@babel/generator": "^7.12.11", + "@babel/helper-function-name": "^7.12.11", + "@babel/helper-split-export-declaration": "^7.12.11", + "@babel/parser": "^7.12.11", + "@babel/types": "^7.12.12", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.12.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", + "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz", + "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-includes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", + "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "get-intrinsic": "^1.0.1", + "is-string": "^1.0.5" + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "array.prototype.flat": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", + "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "bcrypt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.0.tgz", + "integrity": "sha512-jB0yCBl4W/kVHM2whjfyqnxTmOHkCX4kHEa5nYKSoGeYe8YrjTYTc87/6bwt1g8cmV0QrbhKriETg9jWtcREhg==", + "requires": { + "node-addon-api": "^3.0.0", + "node-pre-gyp": "0.15.0" + } + }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "bookshelf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bookshelf/-/bookshelf-1.1.1.tgz", + "integrity": "sha512-swHlUoCSBv7lS5P6Sbc3zTo64L8Yal98H7v/d1pJLrQLZjiJYjzsdEwnBmnbIr2zDEAZhJW3OZSJFj8ps6o9pQ==", + "requires": { + "bluebird": "^3.7.2", + "create-error": "~0.3.1", + "inflection": "^1.12.0", + "lodash": "^4.17.15" + } + }, + "bowser": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", + "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colorette": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.1.0.tgz", + "integrity": "sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg==" + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-security-policy-builder": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", + "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-error": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/create-error/-/create-error-0.3.1.tgz", + "integrity": "sha1-aYECRaYp5lRDK/BDdzYAA6U1GiM=" + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "dasherize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", + "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" + }, + "dns-prefetch-control": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz", + "integrity": "sha512-hvSnros73+qyZXhHFjx2CMLwoj3Fe7eR9EJsFsqmcI1bB2OBWL/+0YzaEaKssCHnj/6crawNnUyw74Gm2EKe+Q==" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dont-sniff-mimetype": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", + "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.18.0-next.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", + "integrity": "sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.1", + "object-inspect": "^1.9.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.3", + "string.prototype.trimstart": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz", + "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.3.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.0", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.20", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", + "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", + "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.4", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", + "dev": true + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "expect-ct": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.2.0.tgz", + "integrity": "sha512-6SK3MG/Bbhm8MsgyJAylg+ucIOU71/FzyFalcfu5nY19dH8y/z0tBJU0wrNBXD4B27EoQtqPF/9wqH0iYAd04g==" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "feature-policy": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", + "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" + }, + "file-entry-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", + "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==" + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "requires": { + "for-in": "^1.0.1" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "frameguard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.1.0.tgz", + "integrity": "sha512-TxgSKM+7LTA6sidjOiSZK9wxY0ffMPY3Wta//MqwmX0nZuEHc8QrkV8Fh3ZhMJeiH+Uyh/tcaarImRy8u77O7g==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "get-intrinsic": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", + "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getopts": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", + "integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "handlebars": { + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", + "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "helmet": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.22.0.tgz", + "integrity": "sha512-Xrqicn2nm1ZIUxP3YGuTBmbDL04neKsIT583Sjh0FkiwKDXYCMUqGqC88w3NUvVXtA75JyR2Jn6jw6ZEMOD+ZA==", + "requires": { + "depd": "2.0.0", + "dns-prefetch-control": "0.2.0", + "dont-sniff-mimetype": "1.1.0", + "expect-ct": "0.2.0", + "feature-policy": "0.3.0", + "frameguard": "3.1.0", + "helmet-crossdomain": "0.4.0", + "helmet-csp": "2.10.0", + "hide-powered-by": "1.1.0", + "hpkp": "2.0.0", + "hsts": "2.2.0", + "ienoopen": "1.1.0", + "nocache": "2.1.0", + "referrer-policy": "1.2.0", + "x-xss-protection": "1.3.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "helmet-crossdomain": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", + "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" + }, + "helmet-csp": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", + "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", + "requires": { + "bowser": "2.9.0", + "camelize": "1.0.0", + "content-security-policy-builder": "2.1.0", + "dasherize": "2.0.0" + } + }, + "hide-powered-by": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", + "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpkp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", + "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" + }, + "hsts": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", + "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", + "requires": { + "depd": "2.0.0" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ienoopen": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.1.0.tgz", + "integrity": "sha512-MFs36e/ca6ohEKtinTJ5VvAJ6oDRAYFdYXweUnGY9L9vcoqFOU4n2ZhmJ0C4z/cwGZ3YIQRSB3XZ1+ghZkY5NQ==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflection": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", + "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "interpret": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.0.0.tgz", + "integrity": "sha512-e0/LknJ8wpMMhTiWcjivB+ESwIuvHnBSlBbmP/pSb8CQJldoj1p2qv7xGZ/+BtbTziYRFSz8OsvdbiX45LtYQA==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonschema": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.6.tgz", + "integrity": "sha512-SqhURKZG07JyKKeo/ir24QnS4/BV7a6gQy93bUSe4lUdNp0QNpIz2c9elWJQ9dpc5cQYY6cvCzgRwy0MQCLyqA==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "knex": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.21.1.tgz", + "integrity": "sha512-uWszXC2DPaLn/YznGT9wFTWUG9+kqbL4DMz+hCH789GLcLuYzq8werHPDKBJxtKvxrW/S1XIXgrTWdMypiVvsw==", + "requires": { + "colorette": "1.1.0", + "commander": "^5.1.0", + "debug": "4.1.1", + "esm": "^3.2.25", + "getopts": "2.2.5", + "inherits": "~2.0.4", + "interpret": "^2.0.0", + "liftoff": "3.1.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "pg-connection-string": "2.2.0", + "tarn": "^3.0.0", + "tildify": "2.0.0", + "uuid": "^7.0.3", + "v8flags": "^3.1.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + } + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "needle": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", + "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "nocache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", + "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" + }, + "node-addon-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.0.0.tgz", + "integrity": "sha512-sSHCgWfJ+Lui/u+0msF3oyCgvdkhxDbkCS6Q8uiJquzOimkJBvX6hl5aSSA7DR1XbMpdM8r7phjcF63sF4rkKg==" + }, + "node-pre-gyp": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", + "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.3", + "needle": "^2.5.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-89ps+SBGpo0D4Bi5ZrxcrCiRFaMmkCt+gItMXQGzEtZVR3uAD3QAQIDoxTWnx3ky0Dwwy/dhFrQ+6NNGXpw/qQ==" + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-bundled": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" + }, + "npm-packlist": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", + "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", + "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", + "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1", + "has": "^1.0.3" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pg-connection-string": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.0.tgz", + "integrity": "sha512-xB/+wxcpFipUZOQcSzcgkjcNOosGhEoPSjz06jC89lv1dj7mc9bZv6wLVy8M2fVjP0a/xN0N988YDq1L0FhK3A==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "^1.1.6" + } + }, + "referrer-policy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", + "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "dev": true, + "requires": { + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ajv": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.0.3.tgz", + "integrity": "sha512-R50QRlXSxqXcQP5SvKUrw8VZeypvo12i2IX0EeR5PiZ7bEKeHWgzgo264LDadUsCU42lTJVhFikTqJwNeH34gQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "tarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.0.tgz", + "integrity": "sha512-PKUnlDFODZueoA8owLehl8vLcgtA8u4dRuVbZc92tspDYZixjJL6TqYOmryf/PfP/EBX+2rgNcrj96NO+RPkdQ==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "uglify-js": { + "version": "3.12.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.12.3.tgz", + "integrity": "sha512-feZzR+kIcSVuLi3s/0x0b2Tx4Iokwqt+8PJM7yRHKuldg4MLdam4TCFeICv+lgDtuYiCtdmrtIP+uN9LWvDasw==", + "optional": true + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.2.0.tgz", + "integrity": "sha512-CYpGiFTUrmI6OBMkAdjSDM0k5h8SkkiTP4WAjQgDgNB1S3Ou9VBEvr6q0Kv2H1mMk7IWfxYGpMH5sd5AvcIV2Q==" + }, + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", + "dev": true + }, + "v8flags": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "x-xss-protection": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", + "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..86ed9a7 --- /dev/null +++ b/package.json @@ -0,0 +1,73 @@ +{ + "name": "auracle-api", + "version": "1.0.0", + "description": "API for Auracle database", + "main": "index.js", + "scripts": { + "start": "node index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/AlexisNP/spellsaurus.git" + }, + "keywords": [ + "api", + "rpg", + "spells", + "magic", + "database", + "ttrpg", + "dices", + "worldbuilding" + ], + "author": "AlexisNP", + "license": "ISC", + "bugs": { + "url": "https://github.com/AlexisNP/spellsaurus/issues" + }, + "homepage": "https://github.com/AlexisNP/spellsaurus#readme", + "dependencies": { + "bcrypt": "^5.0.0", + "bookshelf": "^1.1.1", + "cors": "^2.8.5", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "handlebars": "^4.7.6", + "helmet": "^3.22.0", + "jsonschema": "^1.2.6", + "knex": "^0.21.1", + "morgan": "^1.10.0", + "mysql": "^2.18.1", + "nodemailer": "^6.4.17", + "uuid": "^8.2.0" + }, + "devDependencies": { + "babel-eslint": "^10.1.0", + "eslint": "^7.18.0", + "eslint-plugin-import": "^2.22.1" + }, + "eslintConfig": { + "env": { + "es6": true, + "browser": true + }, + "parser": "babel-eslint", + "rules": { + "accessor-pairs": "warn", + "array-callback-return": "error", + "brace-style": "warn", + "consistent-return": "error", + "default-case": "error", + "eqeqeq": "warn", + "no-case-declarations": "error", + "no-empty-pattern": "warn", + "no-fallthrough": "error", + "no-unused-vars": "error", + "no-useless-catch": "error", + "no-var": "error", + "semi": "warn", + "no-extra-semi": "error", + "yoda": "warn" + } + } +} diff --git a/repositories/ingredient-repository.js b/repositories/ingredient-repository.js new file mode 100644 index 0000000..29150e3 --- /dev/null +++ b/repositories/ingredient-repository.js @@ -0,0 +1,206 @@ +// Bookshelf +const bookshelf = require('../database/bookshelf').bookshelf; +const model = require('../models/ingredient-model'); + +// Model validation +const Validator = require('jsonschema').Validator; +const validator = new Validator(); +const IngredientValidation = require("../validations/IngredientValidation"); +validator.addSchema(IngredientValidation, "/IngredientValidation"); + +// Validations +const isXSSAttempt = require('../functions').isXSSAttempt; +const isEmptyObject = require('../functions').isEmptyObject; + +class IngredientRepository { + + constructor() { + } + + getAll() { + return new Promise((resolve, reject) => { + new model() + .fetchAll({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Il n'existe aucun ingrédient disponible.", + "code": 404, + }); + }); + }); + } + + + getOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'ingrédient en question n'a pas pu être trouvé.", + "code": 404, + }); + }); + }); + } + + getSpellsFromOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Les sortilèges liés à cet ingrédient n'ont pas pu être récupérés.", + "code": 404, + }); + }); + }); + } + + addOne(igr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(igr)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(igr, IngredientValidation).valid) { + reject({ + "message": `Le modèle d'ingrédient n'est pas respecté : ${validator.validate(s, IngredientValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(igr.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + bookshelf.transaction(t => { + return new model({ + 'name': igr.name, + 'description': igr.description, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err; + }); + }) + .then(v => { + return v.load(['spells']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + } + }); + } + + updateOne(id, igr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(igr)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(igr, IngredientValidation).valid) { + reject({ + "message": `Le modèle d'ingrédient n'est pas respecté : ${validator.validate(s, IngredientValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(igr.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + new model({ id: id }) + .fetch({ require: true, withRelated: ['spells'] }) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': igr.name, + 'description': igr.description, + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err); + throw err; + }); + }) + .then(v => { + return v.load(['spells']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'ingrédient en question n'a pas été trouvé.", + "code": 404, + }); + }); + } + }); + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ require: true, withRelated: ['spells'] }) + .then(v => { + v.spells().detach(); + v.destroy(); + }) + .then(() => { + resolve({ + 'message': 'Ingredient with ID ' + id + ' successfully deleted !' + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'ingrédient en question n'a pas été trouvé.", + "code": 404, + }); + }); + }); + } +} + +module.exports = IngredientRepository; \ No newline at end of file diff --git a/repositories/meta-school-repository.js b/repositories/meta-school-repository.js new file mode 100644 index 0000000..1431c67 --- /dev/null +++ b/repositories/meta-school-repository.js @@ -0,0 +1,51 @@ +// Bookshelf +const model = require('../models/meta-school-model') + +// Model validation +const Validator = require('jsonschema').Validator +const validator = new Validator() +const MetaSchoolValidation = require("../validations/MetaSchoolValidation") +validator.addSchema(MetaSchoolValidation, "/MetaSchoolValidation") + +class MetaSchoolRepository { + + constructor() { + } + + getAll() { + return new Promise((resolve, reject) => { + new model() + .fetchAll({ withRelated: ['schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject({ + "message": "Il n'existe aucune grande école disponible.", + "code": 404 + }); + }) + }) + } + + getOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err) + reject({ + "message": "La grande école en question n'a pas pu être trouvée.", + "code": 404 + }); + }) + }) + } +} + +module.exports = MetaSchoolRepository \ No newline at end of file diff --git a/repositories/school-repository.js b/repositories/school-repository.js new file mode 100644 index 0000000..22740e8 --- /dev/null +++ b/repositories/school-repository.js @@ -0,0 +1,207 @@ +// Bookshelf +const bookshelf = require('../database/bookshelf').bookshelf; +const model = require('../models/school-model'); + +// Model validation +const Validator = require('jsonschema').Validator; +const validator = new Validator(); +const SchoolValidation = require("../validations/SchoolValidation"); +validator.addSchema(SchoolValidation, "/SchoolValidation"); + +// Validations +const isXSSAttempt = require('../functions').isXSSAttempt; +const isEmptyObject = require('../functions').isEmptyObject; + +class SchoolRepository { + + constructor() { + } + + getAll() { + return new Promise((resolve, reject) => { + new model() + .fetchAll({ withRelated: ['meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Il n'existe aucune école disponible.", + "code": 404, + }); + }); + }); + } + + getOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'école en question n'a pas pu être trouvée.", + "code": 404, + }); + }); + }); + } + + getSpellsFromOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Les sortilèges de cette école n'ont pas pu être récupérés.", + "code": 404, + }); + }); + }); + } + + addOne(s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(s)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(s, SchoolValidation).valid) { + reject({ + "message": `Le modèle d'école n'est pas respecté : ${validator.validate(s, SchoolValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + bookshelf.transaction(t => { + return new model({ + 'name': s.name, + 'description': s.description, + 'meta_school_id': s.meta_school_id, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err; + }); + }) + .then(v => { + return v.load(['meta_schools']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + } + }); + } + + updateOne(id, s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(s)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(s, SchoolValidation).valid) { + reject({ + "message": `Le modèle d'école n'est pas respecté : ${validator.validate(s, SchoolValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + new model({ id: id }) + .fetch({ require: true, withRelated: ['meta_schools'] }) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': s.name, + 'description': s.description, + 'meta_school_id': s.meta_school_id + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err); + throw err; + }); + }) + .then(v => { + return v.load(['meta_schools']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'école en question n'a pas été trouvée.", + "code": 404, + }); + }); + } + }); + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ require: true, withRelated: ['spells', 'meta_schools'] }) + .then(v => { + v.spells().detach(); + v.destroy(); + }) + .then(() => { + resolve({ + 'message': 'School with ID ' + id + ' successfully deleted !' + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "L'école en question n'a pas été trouvée.", + "code": 404, + }); + }); + }); + } +} + +module.exports = SchoolRepository; \ No newline at end of file diff --git a/repositories/spell-repository.js b/repositories/spell-repository.js new file mode 100644 index 0000000..aa07ddd --- /dev/null +++ b/repositories/spell-repository.js @@ -0,0 +1,351 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/bookshelf').bookshelf +const model = require('../models/spell-model') + +// Model validation +const Validator = require('jsonschema').Validator +const validator = new Validator() +const SpellValidation = require("../validations/SpellValidation") +validator.addSchema(SpellValidation, "/SpellValidation") + +// Validations +const isXSSAttempt = require('../functions').isXSSAttempt +const isEmptyObject = require('../functions').isEmptyObject + +class SpellRepository { + + constructor() { + } + + getAll(name, description, level, charge, cost, ritual) { + return new Promise((resolve, reject) => { + + let query = new model(); + + if (name) { + query.where('name', 'like', `%${name}%`) + } + if (description) { + query.where('description', 'like', `%${description}%`) + } + if (level) { + query.where({ 'level': level }) + } + if (charge) { + query.where({ 'charge': charge }) + } + if (cost) { + query.where({ 'cost': cost }) + } + if (ritual) { + query.where({ 'is_ritual': ritual }) + } + + query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err); + reject({ + "message": "Il n'existe aucun sortilège disponible.", + "code": 404, + }); + }) + }) + } + + getAllPublic(name, description, level, charge, cost, ritual) { + return new Promise((resolve, reject) => { + + let query = new model().where({ 'public': 1 }) + + if (name) { + query.where('name', 'like', `%${name}%`) + } + if (description) { + query.where('description', 'like', `%${description}%`) + } + if (level) { + query.where({ 'level': level }) + } + if (charge) { + query.where({ 'charge': charge }) + } + if (cost) { + query.where({ 'cost': cost }) + } + if (ritual) { + query.where({ 'is_ritual': ritual }) + } + + query.fetchAll({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Il n'existe aucun sortilège disponible.", + "code": 404, + }); + }) + }) + } + + getPage(page) { + return new Promise((resolve, reject) => { + new model() + .where({ 'public': 1 }) + .fetchPage({ + pageSize: 20, + page: page, + withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'], + }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err); + reject({ + "message": "La page de sortilèges n'a pas pu être chargée", + "code": 404, + }); + }) + }) + } + + getOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })) + }) + .catch(err => { + console.log(err); + reject({ + "message": "Le sortilège en question n'a pas été trouvé.", + "code": 404, + }); + }) + }) + } + + addOne(s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(s)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(s, SpellValidation).valid) { + reject({ + "message": `Le modèle de sortilège n'est pas respecté : ${validator.validate(s, SpellValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + bookshelf.transaction(t => { + return new model({ + 'name': s.name, + 'description': s.description, + 'level': s.level, + 'charge': s.charge, + 'cost': s.cost, + 'is_ritual': s.is_ritual + }).save(null, { + transacting: t + }) + .tap(spell => { + return spell + .schools() + .attach(s.schools, { + transacting: t + }); + }) + .tap(spell => { + return spell + .variables() + .attach(s.variables, { + transacting: t + }); + }) + .tap(spell => { + return spell + .ingredients() + .attach(s.ingredients, { + transacting: t + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Un attributs du sortilège a provoqué une erreur d'insertion.", + "code": 500, + }); + }) + }) + .then(v => { + return v.load(['schools.meta_schools', 'variables', 'ingredients', 'author']) + }) + .then(v => { + resolve(this.getOne(v.id)) + }) + .catch(err => { + console.log(err); + reject({ + "message": "Le sortilège n'a pas pu être ajouté.", + "code": 500, + }); + }) + } + }) + } + + updateOne(id, s) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(s)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(s, SpellValidation).valid) { + reject({ + "message": `Le modèle de sortilège n'est pas respecté : ${validator.validate(s, SpellValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(s.name) || isXSSAttempt(s.description) || isXSSAttempt(s.cost)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + new model({ id: id }) + .fetch({ require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] }) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'name': s.name, + 'description': s.description, + 'level': s.level, + 'charge': s.charge, + 'cost': s.cost, + 'is_ritual': s.is_ritual + }, { + method: 'update', + transacting: t + }) + // Detaches AND attaches pivot tables, dw about it + .tap(spell => { + if (s.schools) { + let schools = spell.related('school'); + return spell.schools().detach(schools, { transacting: t }); + } + return spell + }) + .tap(spell => { + if (s.variables) { + let variables = spell.related('variable'); + return spell.variables().detach(variables, { transacting: t }); + } + return spell; + }) + .tap(spell => { + if (s.ingredients) { + let ingredients = spell.related('ingredient'); + return spell.ingredients().detach(ingredients, { transacting: t }); + } + return spell; + }) + .tap(spell => { + return spell + .schools() + .attach(s.schools, { + transacting: t + }); + }) + .tap(spell => { + return spell + .variables() + .attach(s.variables, { + transacting: t + }); + }) + .tap(spell => { + return spell + .ingredients() + .attach(s.ingredients, { + transacting: t + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Un attributs du sortilège a provoqué une erreur d'insertion.", + "code": 500, + }) + }) + }) + .then(v => { + return v.load(['schools.meta_schools', 'variables', 'ingredients', 'author']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur de chargement est survenue.", + "code": 500, + }); + }) + }) + .catch(err => { + console.log(err); + reject({ + "message": "Le sortilège en question n'a pas été trouvé.", + "code": 404, + }); + }) + } + }) + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ require: true, withRelated: ['schools.meta_schools', 'variables', 'ingredients', 'author'] }) + .then(v => { + v.schools().detach(); + v.variables().detach(); + v.ingredients().detach(); + v.destroy(); + }) + .then(() => { + resolve({ + "message": `Le sortilège #${id} a été supprimé avec succès.`, + "code": 200, + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Le sortilège en question n'a pas été trouvé.", + "code": 404, + }); + }) + }) + } +} + +module.exports = SpellRepository \ No newline at end of file diff --git a/repositories/user-repository.js b/repositories/user-repository.js new file mode 100644 index 0000000..6edc212 --- /dev/null +++ b/repositories/user-repository.js @@ -0,0 +1,562 @@ +'use strict' +// Bookshelf +const bookshelf = require('../database/bookshelf').bookshelf; +const model = require('../models/user-model'); +const token_model = require('../models/api-token-model'); + +// Hashing and passwords +const bcrypt = require('bcrypt'); +const { v4: uuidv4 } = require('uuid'); + +// Mailing methods +const mails = require('../smtp/mails'); + +// Model validation +const Validator = require('jsonschema').Validator; +const validator = new Validator(); +const UserValidation = require("../validations/UserValidation"); +validator.addSchema(UserValidation, "/UserValidation"); + +// Validations +const isXSSAttempt = require('../functions').isXSSAttempt; +const isEmptyObject = require('../functions').isEmptyObject; + +class UserRepository { + + constructor() { + } + + /** + * Fetches all users in the dabatase. + * + * @returns { Promise } + * Fulfilled data: Array of user objects. + */ + getAll() { + return new Promise((resolve, reject) => { + new model() + .fetchAll({ withRelated: ['role.permissions'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err) + reject({ + "message": "Erreur de base de données, les utilisateurs n'ont pas pu être récupérés.", + "code": 500, + }); + }) + }) + } + + /** + * Fetches a user object associated with the uuid. + * + * @param { string } uuid + * @param { boolean } full + * Whether the password should also be fetched. (should never be true unless you want to log the user) + * + * @returns { Promise } + * Fulfilled data: Queried user object. + */ + getOneByUUID(uuid, full) { + return new Promise((resolve, reject) => { + + if (!(uuid)) { + reject({ + "message": "La requête doit renseigner un uuid.", + "code": 400, + }) + } + + new model() + .where({ 'uuid': uuid }) + .fetch({ withRelated: ['role.permissions'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true, visibility: !full })); + }) + .catch(() => { + reject({ + "message": "L'utilisateur avec cet UUID n'a pas été trouvé.", + "code": 404, + }); + }) + }) + } + + /** + * Fetches a user object associated with the mail address. + * + * @param { string } mail + * @param { boolean } full + * Whether the password should also be fetched. (should never be true unless you want to log the user) + * + * @returns { Promise } + * Fulfilled data: Queried user object. + */ + getOneByEmail(mail, full) { + return new Promise((resolve, reject) => { + + if (!(mail)) { + reject({ + "message": "La requête doit renseigner un email.", + "code": 400, + }) + } + + new model() + .where({ 'mail': mail }) + .fetch({ withRelated: ['role'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true, visibility: !full })); + }) + .catch(() => { + reject({ + "message": "L'utilisateur avec cet email n'a pas été trouvé.", + "code": 404, + }); + }); + }) + } + + /** + * Fetches all spells linked to a user's uuid + * + * @param { string } uuid + * + * @returns + */ + getSpellsFromOne(uuid) { + return new Promise((resolve, reject) => { + + if (!(uuid)) { + reject({ + "message": "La requête doit renseigner un uuid.", + "code": 400, + }) + } + + new model() + .where({ 'uuid': uuid }) + .fetch({ withRelated: ['role', 'spells.schools.meta_schools', 'spells.variables', 'spells.ingredients'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(() => { + reject({ + "message": "Les sorts liés à cet utilisateur n'ont pas été trouvés.", + "code": 404, + }); + }); + }) + } + + /** + * Registers a user based on the model at ./models/user-model.js. + * + * @param { object} u + * User object + * + * @returns { Promise } + * Fulfilled data: Queried user object. + */ + addOne(u) { + return new Promise(async (resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(u)) { + reject({ + "message": "Le corps de requête ne peut être vide.", + "code": 403, + }) + } else if (!validator.validate(u, UserValidation).valid) { + reject({ + "message": "Structure de la requête invalide - " + validator.validate(u, UserValidation).errors, + "code": 403, + }) + } else if (isXSSAttempt(u.name) || isXSSAttempt(u.password) || isXSSAttempt(u.mail)) { + reject({ + "message": "Essai d'injection détecté, avortement de la requête.", + "code": 403, + }) + } else { + let hash = await bcrypt.hash(u.password, 10); + + let uuid = uuidv4(); + let verification_token = uuidv4(); + + this.checkIfEmailAvailable(u.mail) + .then(() => { + bookshelf.transaction(t => { + return new model({ + 'uuid': uuid, + 'name': u.name, + 'mail': u.mail, + 'password': hash, + 'verification_token': verification_token, + }) + .save(null, { + transacting: t + }) + .catch(err => { + console.log(err); + reject({ + "message": "Un attribut de l'utilisateur a provoqué une erreur d'insertion.", + "code": 500, + }); + }) + }) + .then(() => { + return this.getOneByUUID(uuid, false) + }) + .then(newUser => { + + // Send a mail to the new user's email with a link to verification url + mails.sendRegistrationMail({ + user: { + name: newUser.name, + mail: newUser.mail, + token: verification_token, + } + }); + + // Then resolves the api call + resolve({ + "message": `Compte utilisateur #${newUser.id} créé avec succès.`, + "code": 201, + "user": newUser, + }); + }) + .catch(err => { + console.log(err); + resolve({ + "message": "Une erreur s'est produite en créant votre compte. Veuillez réessayer ultérieurement ou contactez l'administrateur.", + "code": 500, + }) + }) + }) + .catch(err => { + reject(err) + }) + } + }) + } + + /** + * Verifies an account based on a private UUID token + * + * @param { string } token + * A UUID v4 identification token provided at registration and on special demands + * + * @returns Redirects to login page + */ + verifyUser(token) { + return new Promise((resolve, reject) => { + new model() + .where({ 'verification_token': token }) + .fetch() + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'verification_token': null, + 'verified': 1, + }, { + method: 'update', + transacting: t + }) + }) + .then(() => { + resolve({ + "message": "Insérez ici une future redirection vers le client.", + "code": 202, + }) + }) + }) + .catch(() => { + reject({ + "message": "Le lien de vérification ne semble pas correct.", + "code": 404, + }) + }) + }); + } + + /** + * Logs a user by comparing the dual mail/password inputs + * + * @param { string } mail + * @param { string } password + * + * @return { Promise } + * Fulfilled data: Queried user object. + */ + logUser(mail, password) { + return new Promise((resolve, reject) => { + this.getOneByEmail(mail, true) + .then(async fetchedUser => { + + let match = await bcrypt.compare(password, fetchedUser.password); + + // Makes sure no hash gets out + delete fetchedUser.password; + + // If you found a user... + if (match) { + // If they're banned... + if (fetchedUser.banned) { + reject({ + "message": `L'utilisateur #${fetchedUser.name} a été banni, la connexion est impossible.`, + "code": 403, + }); + // If they're not verified... + } else if (!fetchedUser.verified) { + reject({ + "message": `L'utilisateur #${fetchedUser.name} n'as pas été vérifié, le compte doit être activé avant la connexion.`, + "code": 401, + }); + } else { + resolve({ + "message": `L'utilisateur #${fetchedUser.name} s'est connecté.`, + "code": 200, + "user": fetchedUser, + }); + } + } else { + reject({ + "message": "Les informations de connexions sont erronées.", + "code": 400, + }); + } + }) + .catch(err => { + reject(err); + }) + }) + } + + /** + * Generate a token for the user to use the API + * Requires mail and password for verifying the user + * + * @param { string } mail + * @param { string } password + * + * @returns { Promise } + * Fulfilled data: A unique UUID token string. + */ + genAPIToken(mail, password) { + return new Promise((resolve, reject) => { + this.logUser(mail, password) + .then(v => { + let user = v.user; + let new_token = uuidv4(); + + bookshelf.transaction(t => { + return new token_model({ + 'value': new_token, + 'user_uuid': user.uuid, + }) + .save(null, { + transacting: t + }) + .catch(err => { + // If the account already has an API key linked... + if (err.errno === 1062) { + this.fetchAPIKey(user.uuid) + .then(old_api_key => { + reject({ + "message": "Votre compte a déjà généré une clé d'API.", + "code": 409, + "API_key": old_api_key.value, + }); + }); + + // Default errors + } else { + throw err + } + }); + }) + .then(api_key => { + resolve({ + "message": "La clé d'API a été généré.", + "code": 201, + "API_key": api_key, + }) + }) + .catch(err => { + console.log(err); + reject({ + "message": "La génération de jeton d'API n'a pas pu être conclue.", + "code": 500, + }); + }) + }) + .catch(err => { + reject(err); + }) + }) + } + + checkAPITokenPerms(token, permissions) { + return new Promise(async (resolve, reject) => { + + // If the request doesn't have a token, reject it. + if (!token) { + reject({ + "message": "Vous devez utiliser un jeton d'API dans l'en-tête de votre requête.", + "code": 401, + }) + } + + // Fetches user from token + new token_model() + .where({ 'value': token }) + .fetch({ withRelated: 'user.role.permissions' }) + + // Catches not found errors + .catch(err => { + if (err instanceof token_model.NotFoundError) { + reject({ + "message": "Ce jeton n'est affilié à aucun compte.", + "code": 404, + }); + } else { + reject({ + "message": "Le jeton ou l'utilisateur n'a pas pu être récupéré.", + "code": 500, + }); + } + }) + + .then(fullToken => { + token = fullToken.toJSON({ omitPivot: true }); + + // If the token is associated with a banned user... + if (token.user.banned) { + reject({ + "message": "Le jeton est lié à un utilisateur banni, il n'est donc plus utilisable.", + "code": 401, + }); + } + + // If the token is associated with a non-verified user... + if (!token.user.verified) { + reject({ + "message": "Le jeton est lié à un utilisateur non-vérifié, il n'est donc pas utilisable.", + "code": 401, + }); + } + + let user_permissions = token.user.role.permissions; + + // Convert user_perm to array + user_permissions.forEach((user_perm, i) => { + user_permissions[i] = user_perm.slug; + }); + + // Loops to check if the person has all the permissions required... + permissions.forEach(perm => { + // If not, reject... + if (!user_permissions.includes(perm)) { + reject({ + "message": "Permissions insuffisantes.", + "code": 401 + }) + } + }); + + // If everything went well, resolve the promise. + resolve({ + "message": "Credentials accepted.", + "code": 200 + }); + }) + + // Unhandled errors + .catch(() => { + reject({ + "message": "Une erreur inconnue est survenue.", + "code": 500, + }); + }); + }) + } + + /** + * Check if the email that was input is available for account creation. + * + * @param {string} mail + * + * @returns { Promise } + * Fulfilled: HTTP 200 if email is available. + * Rejected: HTTP 400-409 if email is already used. + */ + checkIfEmailAvailable(mail) { + return new Promise((resolve, reject) => { + + if (!(mail)) { + reject({ + "message": "La requête doit renseigner un email.", + "code": 400, + }) + } + + if (!this.validateMail(mail)) { + reject({ + "message": "La requête n'est pas un email valide.", + "code": 400, + }) + } + + this.getOneByEmail(mail, false) + .then(() => { + reject({ + "message": "Cet email est déjà lié à un compte.", + "code": 409, + }) + }) + .catch(() => { + resolve({ + "message": "Cet email est disponible.", + "code": 200, + }) + }) + }) + } + + /** + * Fetches the associated api_token from a user uuid. + * + * @param { string } uuid + * + * @returns { Promise } + * Fulfilled data: Queried API token object + */ + fetchAPIKey(uuid) { + return new Promise((resolve, reject) => { + new token_model() + .where({ 'user_uuid': uuid }) + .fetch() + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + reject(err); + }) + }) + } + + /** + * Whether a mail is correctly formed and ripe for receiving, ie: xxx@yyy.zzz. + * + * @param { string } mail + * + * @returns { boolean } + */ + validateMail(mail) { + const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return regex.test(String(mail).toLowerCase()); + } +} + +module.exports = UserRepository \ No newline at end of file diff --git a/repositories/variable-repository.js b/repositories/variable-repository.js new file mode 100644 index 0000000..69192cb --- /dev/null +++ b/repositories/variable-repository.js @@ -0,0 +1,204 @@ +// Bookshelf +const bookshelf = require('../database/bookshelf').bookshelf; +const model = require('../models/variable-model'); + +// Model validation +const Validator = require('jsonschema').Validator; +const validator = new Validator(); +const VariableValidation = require("../validations/VariableValidation"); +validator.addSchema(VariableValidation, "/VariableValidation"); + +// Validations +const isXSSAttempt = require('../functions').isXSSAttempt; +const isEmptyObject = require('../functions').isEmptyObject; + +class VariableRepository { + + constructor() { + } + + getAll() { + return new Promise((resolve, reject) => { + new model() + .fetchAll({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Il n'existe aucune variable disponible.", + "code": 404, + }); + }); + }); + } + + + getOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['spells'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "La variable en question n'a pas pu être trouvée.", + "code": 404, + }); + }); + }); + } + + getSpellsFromOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ withRelated: ['spells', 'spells.schools', 'spells.variables', 'spells.ingredients', 'spells.schools.meta_schools'] }) + .then(v => { + resolve(v.toJSON({ omitPivot: true })); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Les sortilèges liés à cette variable n'ont pas pu être récupérés.", + "code": 404, + }); + }); + }); + } + + addOne(vr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(vr)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(vr, VariableValidation).valid) { + reject({ + "message": `Le modèle de variable n'est pas respecté : ${validator.validate(s, VariableValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(vr.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + bookshelf.transaction(t => { + return new model({ + 'description': vr.description, + }).save(null, { + transacting: t + }) + .catch(err => { + throw err; + }); + }) + .then(v => { + return v.load(['spells']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + } + }); + } + + updateOne(id, vr) { + return new Promise((resolve, reject) => { + // Checks if body exists and if the model fits, and throws errors if it doesn't + if (isEmptyObject(vr)) { + reject({ + "message": "Le corps de la requête ne peut pas être vide.", + "code": 403, + }); + } else if (!validator.validate(vr, VariableValidation).valid) { + reject({ + "message": `Le modèle de variable n'est pas respecté : ${validator.validate(s, VariableValidation).errors}`, + "code": 403, + }); + } else if (isXSSAttempt(vr.description)) { + reject({ + "message": "Tentative d'injection XSS détectée, requête refusée.", + "code": 403, + }); + } else { + new model({ id: id }) + .fetch({ require: true, withRelated: ['spells'] }) + .then(v => { + bookshelf.transaction(t => { + return v.save({ + 'description': vr.description, + }, { + method: 'update', + transacting: t + }) + .catch(err => { + console.log(err); + throw err; + }); + }) + .then(v => { + return v.load(['spells']); + }) + .then(v => { + resolve(this.getOne(v.id)); + }) + .catch(err => { + console.log(err); + reject({ + "message": "Une erreur d'insertion s'est produite.", + "code": 500, + }); + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "La variable en question n'a pas été trouvée.", + "code": 404, + }); + }); + } + }); + } + + deleteOne(id) { + return new Promise((resolve, reject) => { + new model() + .where({ 'id': id }) + .fetch({ require: true, withRelated: ['spells'] }) + .then(v => { + v.spells().detach(); + v.destroy(); + }) + .then(() => { + resolve({ + 'message': 'Variable with ID ' + id + ' successfully deleted !' + }); + }) + .catch(err => { + console.log(err); + reject({ + "message": "La variable en question n'a pas été trouvée.", + "code": 404, + }); + }); + }); + } +} + +module.exports = VariableRepository; \ No newline at end of file diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..16a23b4 --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,28 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// Repository +const UserRepository = require('../repositories/user-repository'); +const Users = new UserRepository(); + +// ROUTES +// GEN API TOKEN +const generateAPIToken = (mail, password) => { + return Users.genAPIToken(mail, password) + .catch(err => { + throw err; + }); +}; +router.get('/genToken', async (req, res) => { + generateAPIToken(req.body.mail, req.body.password) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify(err)); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..01c4109 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,18 @@ +const spells = require('./spells'); +const schools = require('./schools'); +const meta_schools = require('./meta_schools'); +const variables = require('./variables'); +const ingredients = require('./ingredients'); +const users = require('./users'); + +const auth = require('./auth'); + +module.exports = { + auth, + spells, + schools, + meta_schools, + ingredients, + variables, + users, +}; \ No newline at end of file diff --git a/routes/ingredients.js b/routes/ingredients.js new file mode 100644 index 0000000..c2e8ce5 --- /dev/null +++ b/routes/ingredients.js @@ -0,0 +1,183 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// AuthGuard +const authGuard = require('./middleware/authGuard'); + +// Repository +const IngredientRepository = require('../repositories/ingredient-repository'); +const Ingredients = new IngredientRepository(); + +// Functions +const functions = require('../functions'); + +// ROUTES +// GET ALL ------------------ +const getIngredients = () => { + return Ingredients.getAll() + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/', + async (req, res) => { + getIngredients() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET ONE ------------------ +const getIngredient = (id) => { + return Ingredients.getOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/', + async (req, res) => { + getIngredient(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET SPELLS FROM ONE ------------------ +const getSpellsFromOne = (id) => { + return Ingredients.getSpellsFromOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/spells', + async (req, res) => { + getSpellsFromOne(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// CREATE ONE ------------------ +const addIngredient = (igr) => { + return Ingredients.addOne(igr) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.post( + '/', + authGuard(['SUBMIT_INGREDIENTS']), + async (req, res) => { + addIngredient(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// UPDATE ONE ------------------ +const updateIngredient = (id, igr) => { + return Ingredients.updateOne(id, igr) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.put( + '/:id/', + authGuard(['SUBMIT_INGREDIENTS', 'MODIFY_INGREDIENTS']), + async (req, res) => { + updateIngredient(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// DELETE ONE ------------------ +const deleteIngredient = (id) => { + return Ingredients.deleteOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.delete( + '/:id/', + authGuard(['SUBMIT_INGREDIENTS', 'MODIFY_INGREDIENTS', 'DELETE_INGREDIENTS']), + async (req, res) => { + deleteIngredient(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// Param validations +router.param('id', functions.paramIntCheck); + +module.exports = router; \ No newline at end of file diff --git a/routes/meta_schools.js b/routes/meta_schools.js new file mode 100644 index 0000000..587de24 --- /dev/null +++ b/routes/meta_schools.js @@ -0,0 +1,65 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// Functions +const functions = require('../functions'); + +// Repository +const MetaSchoolRepository = require('../repositories/meta-school-repository'); +const MetaSchools = new MetaSchoolRepository(); + +// ROUTES +// GET ALL ------------------ +const getMetaSchools = () => { + return MetaSchools.getAll() + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get('/', async (req, res) => { + getMetaSchools() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + + +// GET ONE ------------------ +const getMetaSchool = (id) => { + return MetaSchools.getOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get('/:id/', async (req, res) => { + getMetaSchool(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + +// Param validations +router.param('id', functions.paramIntCheck); + +module.exports = router; \ No newline at end of file diff --git a/routes/middleware/authGuard.js b/routes/middleware/authGuard.js new file mode 100644 index 0000000..ec951b2 --- /dev/null +++ b/routes/middleware/authGuard.js @@ -0,0 +1,23 @@ +// Repository +const UserRepository = require('../../repositories/user-repository'); +const Users = new UserRepository(); + +// AUTHGUARD +const authGuard = (permissions) => { + return async (req, res, next) => { + + // Get token from headers + let api_token = req.headers['auracle_key']; + + // Uses repo to validate the associated perms with the token + Users.checkAPITokenPerms(api_token, permissions) + .then(() => { + next(); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify(err)); + }); + }; +}; + +module.exports = authGuard; \ No newline at end of file diff --git a/routes/schools.js b/routes/schools.js new file mode 100644 index 0000000..306e917 --- /dev/null +++ b/routes/schools.js @@ -0,0 +1,182 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// AuthGuard +const authGuard = require('./middleware/authGuard'); + +// Repository +const SchoolRepository = require('../repositories/school-repository'); +const Schools = new SchoolRepository(); + +// Functions +const functions = require('../functions'); + +// ROUTES +// GET ALL ------------------ +const getSchools = () => { + return Schools.getAll() + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/', + async (req, res) => { + getSchools() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET ONE ------------------ +const getSchool = (id) => { + return Schools.getOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/', + async (req, res) => { + getSchool(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET SPELLS FROM ONE ------------------ +const getSpellsFromOne = (id) => { + return Schools.getSpellsFromOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/spells', + async (req, res) => { + getSpellsFromOne(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// CREATE ONE ------------------ +const addSchool = (s) => { + return Schools.addOne(s) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.post( + '/', + authGuard(['SUBMIT_SCHOOL']), + async (req, res) => { + addSchool(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// UPDATE ONE ------------------ +const updateSchool = (id, s) => { + return Schools.updateOne(id, s) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.put( + '/:id/', + authGuard(['SUBMIT_SCHOOLS', 'MODIFY_SCHOOLS']), + async (req, res) => { + updateSchool(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// DELETE ONE ------------------ +const deleteSchool = (id) => { + return Schools.deleteOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.delete( + '/:id/', + authGuard(['SUBMIT_SCHOOLS', 'MODIFY_SCHOOLS', 'DELETE_SCHOOLS']), + async (req, res) => { + deleteSchool(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// Param validations +router.param('id', functions.paramIntCheck); + +module.exports = router; \ No newline at end of file diff --git a/routes/spells.js b/routes/spells.js new file mode 100644 index 0000000..151d31e --- /dev/null +++ b/routes/spells.js @@ -0,0 +1,209 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// AuthGuard +const authGuard = require('./middleware/authGuard'); + +// Repository +const SpellReposity = require('../repositories/spell-repository'); +const Spells = new SpellReposity(); + +// Functions +const functions = require('../functions'); + +// ROUTES +// GET ALL PUBLIC ------------------ +const getPublicSpells = (name, description, level, charge, cost, ritual) => { + return Spells.getAllPublic(name, description, level, charge, cost, ritual) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '//:name?/:description?/:level?/:charge?/:cost?/:ritual?/', + async (req, res) => { + getPublicSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// GET ALL ------------------ +const getSpells = (name, description, level, charge, cost, ritual) => { + return Spells.getAll(name, description, level, charge, cost, ritual) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/private/:name?/:description?/:level?/:charge?/:cost?/:ritual?/', + authGuard(['SECRET_SPELLS']), + async (req, res) => { + getSpells(req.query.name, req.query.description, req.query.level, req.query.charge, req.query.cost, req.query.ritual) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// GET SOME ------------------ +const getSomeSpells = (page) => { + return Spells.getPage(page) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/page/:page', + async (req, res) => { + getSomeSpells(req.params.page) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// GET ONE ------------------ +const getSpell = (id) => { + return Spells.getOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/', + async (req, res) => { + getSpell(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// CREATE ONE ------------------ +const addSpell = (s) => { + return Spells.addOne(s) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.post( + '/', + authGuard(['SUBMIT_SPELLS']), + async (req, res) => { + addSpell(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// UPDATE ONE ------------------ +const updateSpell = (id, s) => { + return Spells.updateOne(id, s) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.put( + '/:id/', + authGuard(['SUBMIT_SPELLS', 'MODIFY_SPELLS']), + async (req, res) => { + updateSpell(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// DELETE ONE ------------------ +const deleteSpell = (id) => { + return Spells.deleteOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.delete( + '/:id/', + authGuard(['SUBMIT_SPELLS', 'MODIFY_SPELLS', 'DELETE_SPELLS']), + async (req, res) => { + deleteSpell(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// Param validations +router.param('id', functions.paramIntCheck); +router.param('page', functions.paramIntCheck); + +module.exports = router; diff --git a/routes/users.js b/routes/users.js new file mode 100644 index 0000000..754bba6 --- /dev/null +++ b/routes/users.js @@ -0,0 +1,178 @@ +// Router +const express = require('express'); +let router = express.Router(); + +// AuthGuard +// const authGuard = require('./middleware/authGuard'); + +// Repository +const UserRepository = require('../repositories/user-repository'); +const Users = new UserRepository(); + +// ROUTES +// GET ALL ------------------ +const getUsers = () => { + return Users.getAll() + .catch(err => { + throw err; + }); +}; +router.get('/', async (req, res) => { + getUsers() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + + +// GET ONE FROM UUID ------------------ +const getUserByUUID = (uuid) => { + return Users.getOneByUUID(uuid) + .catch(err => { + throw err; + }); +}; +router.get('/:uuid/', async (req, res) => { + getUserByUUID(req.params.uuid) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + +// GET SPELLS FROM ONE ------------------ +const getSpellsFromUser = (uuid) => { + return Users.getSpellsFromOne(uuid) + .catch(err => { + throw err; + }); +}; +router.get('/:uuid/spells', async (req, res) => { + getSpellsFromUser(req.params.uuid) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + + +// CHECK IF MAIL IS AVAILABLE ------------------ +const checkIfEmailAvailable = (mail) => { + return Users.checkIfEmailAvailable(mail) + .catch(err => { + throw err; + }); +}; +router.get('/available/:mail/', async (req, res) => { + checkIfEmailAvailable(req.params.mail) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + + +// CREATE ONE ------------------ +const addUser = (u) => { + return Users.addOne(u) + .catch(err => { + throw err; + }); +}; +router.post('/', async (req, res) => { + addUser(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + + +// VERIFY A USER +const verifyUser = (token) => { + return Users.verifyUser(token) + .catch(err => { + throw err; + }); +}; +router.get('/verification/:token', async (req, res) => { + verifyUser(req.params.token) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + +// LOG A USER ------------------ +const logUser = (mail, password) => { + return Users.logUser(mail, password) + .catch(err => { + throw err; + }); +}; +router.post('/login', async (req, res) => { + logUser(req.body.mail, req.body.password) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/routes/variables.js b/routes/variables.js new file mode 100644 index 0000000..30a5e90 --- /dev/null +++ b/routes/variables.js @@ -0,0 +1,185 @@ +'use strict'; + +// Router +const express = require('express'); +let router = express.Router(); + +// AuthGuard +const authGuard = require('./middleware/authGuard'); + +// Repository +const VariableRepository = require('../repositories/variable-repository'); +const Variables = new VariableRepository(); + +// Functions +const functions = require('../functions'); + +// ROUTES +// GET ALL ------------------ +const getvariables = () => { + return Variables.getAll() + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/', + async (req, res) => { + getvariables() + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET ONE ------------------ +const getVariable = (id) => { + return Variables.getOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/', + async (req, res) => { + getVariable(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// GET SPELLS FROM ONE ------------------ +const getSpellsFromOne = (id) => { + return Variables.getSpellsFromOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.get( + '/:id/spells', + async (req, res) => { + getSpellsFromOne(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.end(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// CREATE ONE ------------------ +const addVariable = (vr) => { + return Variables.addOne(vr) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.post( + '/', + authGuard(['SUBMIT_VARIABLES']), + async (req, res) => { + addVariable(req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// UPDATE ONE ------------------ +const updateVariable = (id, vr) => { + return Variables.updateOne(id, vr) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.put( + '/:id/', + authGuard(['SUBMIT_VARIABLES', 'MODIFY_VARIABLES']), + async (req, res) => { + updateVariable(req.params.id, req.body) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + + +// DELETE ONE ------------------ +const deleteVariable = (id) => { + return Variables.deleteOne(id) + .catch(err => { + console.log(err); + throw err; + }); +}; +router.delete( + '/:id/', + authGuard(['SUBMIT_VARIABLES', 'MODIFY_VARIABLES', 'DELETE_VARIABLES']), + async (req, res) => { + deleteVariable(req.params.id) + .then(v => { + res.setHeader('Content-Type', 'application/json;charset=utf-8'); + res.send(JSON.stringify(v)); + }) + .catch(err => { + res.status(err.code).send(JSON.stringify( + { + "error": err.message, + "code": err.code + }) + ); + }); + }); + +// Param validations +router.param('id', functions.paramIntCheck); + +module.exports = router; \ No newline at end of file diff --git a/smtp/config.js b/smtp/config.js new file mode 100644 index 0000000..fe58640 --- /dev/null +++ b/smtp/config.js @@ -0,0 +1,14 @@ +const nodemailer = require('nodemailer'); + +const transport = nodemailer.createTransport({ + host: "smtp.mailtrap.io", + port: 2525, + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + } +}); + +module.exports = { + transport, +}; \ No newline at end of file diff --git a/smtp/mails.js b/smtp/mails.js new file mode 100644 index 0000000..b267b1d --- /dev/null +++ b/smtp/mails.js @@ -0,0 +1,61 @@ +const smtp = require('./config'); +const fs = require('fs'); +const handlebars = require('handlebars'); + +// Sender address for mail service +const sender = 'tymos@auracle.io'; + +// Fetches a HTML template file for parsing +const getTemplateFile = (path) => { + return new Promise((resolve, reject) => { + fs.readFile(path, { encoding: 'utf-8' }, (err, html) => { + if (err) { + reject(err); + } else { + resolve(html); + } + }); + }); +}; + +/** + * SEND REGISTRATION MAIL FUNCTION + * @param {Object} data + * - user + * - name + * - mail + * - token + */ +const sendRegistrationMail = (data) => { + getTemplateFile(__dirname + '/templates/template-sign-up.html') + .then(html => { + let template = handlebars.compile(html); + let template_parsed = template(data); + + let message = { + from: sender, + to: data.user.mail, + subject: 'Inscription Auracle.io', + html: template_parsed, + }; + + smtp.transport.sendMail(message, (err, info) => { + if (err) { + throw err; + } else { + console.log(info); + } + }); + }) + .catch(err => { + console.log(err); + }); +}; + +// const sendBanEmail = (date) => { +// return null; +// }; + +module.exports = { + sendRegistrationMail, +}; \ No newline at end of file diff --git a/smtp/templates/template-sign-up.html b/smtp/templates/template-sign-up.html new file mode 100644 index 0000000..1b15b5b --- /dev/null +++ b/smtp/templates/template-sign-up.html @@ -0,0 +1,114 @@ + + + + + Template Email Auracle Inscription + + + + +
+
+
+

Bienvenue sur Auracle, {{ user.name }}!

+
+
+

Votre compte Auracle a bien été enregistré et est rattaché à cette adresse mail.

+

Cependant, afin de garantir la sécurité de vos données, votre compte doit être vérifié avant votre + première connexion. Vous pouvez + + suivre ce lien afin de confirmer votre inscription. + +

+

Si vous rencontrez des problèmes, n'hésitez pas à envoyer un mail à + + support@auracle.io + +

+

Merci de votre soutien !

+

Bien sincèrement, Izàc Tymos.

+
+ +
+
+ + + \ No newline at end of file diff --git a/validations/IngredientValidation.js b/validations/IngredientValidation.js new file mode 100644 index 0000000..962b6f9 --- /dev/null +++ b/validations/IngredientValidation.js @@ -0,0 +1,11 @@ +const Ingredient = { + "id": "/IngredientValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" } + }, + "required": ["name", "description"] +}; + +module.exports = Ingredient; \ No newline at end of file diff --git a/validations/MetaSchoolValidation.js b/validations/MetaSchoolValidation.js new file mode 100644 index 0000000..44b1848 --- /dev/null +++ b/validations/MetaSchoolValidation.js @@ -0,0 +1,12 @@ +const MetaSchool = { + "id": "/MetaSchoolValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "schools": { "type": "array" } + }, + "required": ["name", "description"] +}; + +module.exports = MetaSchool; \ No newline at end of file diff --git a/validations/SchoolValidation.js b/validations/SchoolValidation.js new file mode 100644 index 0000000..03f0148 --- /dev/null +++ b/validations/SchoolValidation.js @@ -0,0 +1,12 @@ +const School = { + "id": "/SchoolValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "meta_school_id": { "type": "number" }, + }, + "required": ["name", "description", "meta_school_id"] +}; + +module.exports = School; \ No newline at end of file diff --git a/validations/SpellValidation.js b/validations/SpellValidation.js new file mode 100644 index 0000000..c440a6c --- /dev/null +++ b/validations/SpellValidation.js @@ -0,0 +1,39 @@ +const Spell = { + "id": "/SpellValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "description": { "type": "string" }, + "level": { "type": "number" }, + "charge": { "type": "number" }, + "cost": { "type": "string" }, + "is_ritual": { "type": "boolean" }, + "schools": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "variables": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + }, + "ingredients": { + "type": { "type": "array" }, + "items": { + "properties": { + "id": { "type": "number" }, + } + } + } + }, + "required": ["name", "description"] +}; + +module.exports = Spell; \ No newline at end of file diff --git a/validations/UserValidation.js b/validations/UserValidation.js new file mode 100644 index 0000000..c6dd4e9 --- /dev/null +++ b/validations/UserValidation.js @@ -0,0 +1,12 @@ +const User = { + "id": "/UserValidation", + "type": Object, + "properties": { + "name": { "type": "string" }, + "mail": { "type": "string" }, + "password": { "type": "string" }, + }, + "required": ["name", "password", "mail"] +}; + +module.exports = User; \ No newline at end of file diff --git a/validations/VariableValidation.js b/validations/VariableValidation.js new file mode 100644 index 0000000..72e0928 --- /dev/null +++ b/validations/VariableValidation.js @@ -0,0 +1,10 @@ +const Variable = { + "id": "/VariableValidation", + "type": Object, + "properties": { + "description": { "type": "string" }, + }, + "required": ["description"] +}; + +module.exports = Variable; \ No newline at end of file