├── .env.example ├── .gitignore ├── .nowignore ├── .sentryclirc ├── LICENSE ├── Makefile ├── README.md ├── api ├── bot.ts ├── github-code-review-notifier.ts ├── github.ts ├── index.ts ├── interactive-components.ts ├── oauth.ts └── paths.ts ├── extra.d.ts ├── global.d.ts ├── now.json ├── package-lock.json ├── package.json ├── src ├── bot.ts ├── config.ts ├── db.ts ├── format.ts ├── github.ts ├── index.ts ├── proxy.ts └── utils.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | VERIFICATION_TOKEN= 2 | BOT_TOKEN= 3 | FIREBASE= 4 | CLIENT_SECRET= 5 | SIGNING_SECRET= 6 | CLIENT_ID= 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | node_modules 4 | now-secrets.json 5 | build 6 | 7 | .env 8 | -------------------------------------------------------------------------------- /.nowignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .git 3 | node_modules 4 | 5 | now-secrets.json 6 | -------------------------------------------------------------------------------- /.sentryclirc: -------------------------------------------------------------------------------- 1 | [defaults] 2 | org=enix 3 | project=github-code-review-notifier 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | GitHub code review notifier, sends you messages about CR on Slack. 635 | Copyright (C) 2018 EnixCoda 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | GitHub code review notifier Copyright (C) 2018 EnixCoda 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GIT_SHA=$(shell git rev-parse HEAD) 2 | VERSION=$(GIT_SHA) 3 | 4 | release: 5 | echo "Got version ${VERSION}" 6 | yarn sentry-cli releases new "${VERSION}" 7 | # Commit Integration 8 | # yarn sentry-cli releases set-commits --auto ${VERSION} 9 | yarn sentry-cli releases set-commits ${VERSION} --commit "EnixCoda/github-code-review-notifier@${GIT_SHA}" 10 | # Emitting sourcemap 11 | # yarn sentry-cli releases files "${VERSION}" upload-sourcemaps dist --no-rewrite 12 | yarn sentry-cli releases finalize "${VERSION}" 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Reviewer Notifier on Slack 2 | 3 | **⚠️ If you are here because this app stopped working, please replace the `now.sh` with `vercel.app` in your webhook URL.** 4 | 5 | ```diff 6 | -https://github-code-review-notifier.now.sh/github?workspace=XXX 7 | +https://github-code-review-notifier.vercel.app/github?workspace=XXX 8 | ``` 9 | --- 10 | 11 | When requested to review pull request on GitHub or when your pull request has been approved, you will be notified on Slack immediately! And a direct link to the Pull Request will be provided in the message. For example, 12 | 13 | ![A picture is worths a thousand words](https://user-images.githubusercontent.com/7480839/56017581-4fcb9200-5d32-11e9-93dc-bd9f3b25a4d0.png) 14 | 15 | ## Setup 16 | 17 | ### Connect Slack workspace 18 | 19 | 1. To add `@GitHub Code Review Notifier` to your workspace, click the button below to Authorize. 20 | 21 | Add to Slack 22 | 23 | 2. Then you could find `@GitHub Code Review Notifier` in your workspace. 24 | 25 | 3. DM anything (e.g. `hi`) to it to get **menu**. 26 | 27 | ### Connect GitHub account 28 | 29 | Click `Link` in the menu to link your GitHub account to the Slack workspace. Only linked users will get notifications. So don't forget to tell your teammates about this tool. 30 | 31 | ### Connect GitHub projects 32 | 33 | You can click `Setup GitHub projects` in the menu to get a webhook for connecting your GitHub projects. 34 | 35 | - [How to use GitHub webhook?](https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/creating-webhooks) 36 | - The events sent to it should at least include `Pull requests` and `Pull request reviews`. Or `Send me everything` for easier setup. 37 | - The URL is bound to the workspace. And multiple projects can share one URL in one Slack workspace. 38 | 39 | ## TODO 40 | - Notify users mentioned in new comment messages 41 | - Notify all users in a review thread when it got reply 42 | - Notify author about all rest messages 43 | 44 | ## Help 45 | 46 | Need help? [Draft an issue](https://github.com/EnixCoda/github-code-review-notifier/issues). 47 | -------------------------------------------------------------------------------- /api/bot.ts: -------------------------------------------------------------------------------- 1 | import { handleBotMessages } from '../src/bot' 2 | import { requestHandler } from '../src/index' 3 | 4 | export default requestHandler(handleBotMessages) 5 | -------------------------------------------------------------------------------- /api/github-code-review-notifier.ts: -------------------------------------------------------------------------------- 1 | import { RequestListener } from '../extra' 2 | import { proxy } from '../src/proxy' 3 | 4 | export const handleAsset: RequestListener = function handleAsset(req, res) { 5 | proxy.web(req, res, { 6 | // TODO: decoupling 7 | target: 'https://enixcoda.github.io/', 8 | }) 9 | } 10 | 11 | export default handleAsset 12 | -------------------------------------------------------------------------------- /api/github.ts: -------------------------------------------------------------------------------- 1 | import { handleGitHubHook } from '../src/github' 2 | import { requestHandler } from '../src/index' 3 | 4 | export default requestHandler(handleGitHubHook) 5 | -------------------------------------------------------------------------------- /api/index.ts: -------------------------------------------------------------------------------- 1 | import { RequestListener } from '../extra' 2 | import { proxy } from '../src/proxy' 3 | 4 | export const handleHome: RequestListener = function handleHome(req, res) { 5 | proxy.web(req, res, { 6 | // TODO: decoupling 7 | target: 'https://enixcoda.github.io/github-code-review-notifier/', 8 | }) 9 | } 10 | 11 | export default handleHome 12 | -------------------------------------------------------------------------------- /api/interactive-components.ts: -------------------------------------------------------------------------------- 1 | import { handleInteractiveComponents } from '../src/bot' 2 | import { requestHandler } from '../src/index' 3 | 4 | export default requestHandler(handleInteractiveComponents) 5 | -------------------------------------------------------------------------------- /api/oauth.ts: -------------------------------------------------------------------------------- 1 | import { handleOAuth } from '../src/bot' 2 | import { requestHandler } from '../src/index' 3 | 4 | export default requestHandler(handleOAuth) 5 | -------------------------------------------------------------------------------- /api/paths.ts: -------------------------------------------------------------------------------- 1 | export const paths = { 2 | GitHub: '/github', 3 | bot: '/bot', 4 | interactive: '/interactive-components', 5 | oauth: '/oauth', 6 | home: '/', 7 | githubAssets: '/github-code-review-notifier', // asset path prefix on GitHub pages 8 | } 9 | -------------------------------------------------------------------------------- /extra.d.ts: -------------------------------------------------------------------------------- 1 | import { NowRequest, NowResponse } from '@now/node' 2 | 3 | export type IncomingMessage = NowRequest 4 | 5 | export type RequestListener = (req: NowRequest, res: NowResponse) => void 6 | -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | type ExpectedAny = any 2 | 3 | // GitHub-Slack Link 4 | type GSLink = { 5 | slack: string // Slack ID 6 | github: string // GitHub username 7 | } 8 | 9 | type WorkspaceMeta = { 10 | accessToken: string 11 | botToken: string 12 | botID: string 13 | } 14 | 15 | type Log = { 16 | time?: string 17 | path?: string 18 | info?: string 19 | data?: ExpectedAny 20 | } 21 | 22 | type Schema = { 23 | registered: { 24 | [workspace: string]: WorkspaceMeta 25 | } 26 | link: { 27 | [workspace: string]: GSLink[] 28 | } 29 | log: { 30 | [workspace: string]: Log[] 31 | } 32 | } 33 | 34 | type SlackPayload = any 35 | type SlackElement = any 36 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-code-review-notifier", 3 | "env": { 4 | "SENTRY_PUBLIC_KEY": "@sentry_public_key", 5 | "SENTRY_PROJECT_ID": "@sentry_project_id", 6 | "FIREBASE": "@firebase", 7 | "VERIFICATION_TOKEN": "@verification_token", 8 | "CLIENT_SECRET": "@client_secret", 9 | "CLIENT_ID": "@client_id", 10 | "SIGNING_SECRET": "@signing_secret", 11 | "LOG_REQUEST_ON_ERROR": "@log_request_on_error", 12 | "NODE_ENV": "production" 13 | }, 14 | "routes": [ 15 | { "src": "/oauth/?", "dest": "/api/oauth.ts" }, 16 | { "src": "/bot/?", "dest": "/api/bot.ts" }, 17 | { "src": "/github/?", "dest": "/api/github.ts" }, 18 | { "src": "/interactive-components/?", "dest": "/api/interactive-components.ts" }, 19 | { "src": "/github-code-review-notifier/(.*)", "dest": "/api/github-code-review-notifier.ts" }, 20 | { "src": "/", "dest": "/api/index.ts" } 21 | ], 22 | "alias": "github-code-review-notifier.now.sh" 23 | } 24 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-code-review-notifier", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@firebase/app": { 8 | "version": "0.3.17", 9 | "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.3.17.tgz", 10 | "integrity": "sha512-/8lDeeIxgdCIMffrfBPQ3bcdSkF8bx4KCp8pKMPOG/HYKoeM8I9eP4zlzxL5ABzRjvcdhK9KOYOn0jRrNrGD9g==", 11 | "requires": { 12 | "@firebase/app-types": "0.3.10", 13 | "@firebase/util": "0.2.14", 14 | "dom-storage": "2.1.0", 15 | "tslib": "1.9.3", 16 | "xmlhttprequest": "1.8.0" 17 | } 18 | }, 19 | "@firebase/app-types": { 20 | "version": "0.3.10", 21 | "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.3.10.tgz", 22 | "integrity": "sha512-l+5BJtSQopalBXiY/YuSaB9KF9PnDj37FLV0Sx3qJjh5B3IthCuZbPc1Vpbbbee/QZgudl0G212BBsUMGHP+fQ==" 23 | }, 24 | "@firebase/auth": { 25 | "version": "0.10.2", 26 | "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.10.2.tgz", 27 | "integrity": "sha512-+S8RZcHhhat2xrW/RGOcSZO8pv0qHveaw09Bq/gXhZyJfN86UeiMc3sv4YMo1Hu7fRRorNteijpmlH522eI0AA==", 28 | "requires": { 29 | "@firebase/auth-types": "0.6.1" 30 | } 31 | }, 32 | "@firebase/auth-types": { 33 | "version": "0.6.1", 34 | "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.6.1.tgz", 35 | "integrity": "sha512-uciPeIQJC1NZDhI5+BWbyqi70YXIjT3jm03sYtIgkPt2sr3n8sq1RpnoTMYfAJkQ0QlgLaBkeM/huMx06eBoXQ==" 36 | }, 37 | "@firebase/database": { 38 | "version": "0.3.20", 39 | "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.3.20.tgz", 40 | "integrity": "sha512-fZHRIlRQlND/UrzI1beUTRKfktjMvMEiUOar6ylFZqOj2KNVO4CrF95UGqRl0HBGhZzlBKzaDYAcJze2D6C4+Q==", 41 | "requires": { 42 | "@firebase/database-types": "0.3.11", 43 | "@firebase/logger": "0.1.13", 44 | "@firebase/util": "0.2.14", 45 | "faye-websocket": "0.11.1", 46 | "tslib": "1.9.3" 47 | } 48 | }, 49 | "@firebase/database-types": { 50 | "version": "0.3.11", 51 | "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-0.3.11.tgz", 52 | "integrity": "sha512-iRAZzs7Zlmmvh7r0XlR1MAO6I6bm1HjW9m1ytfJ6E/8+zItHnbVH4iiVVkC39r1wMGrtPMz8FiIUWoaasPF5dA==" 53 | }, 54 | "@firebase/firestore": { 55 | "version": "1.2.2", 56 | "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-1.2.2.tgz", 57 | "integrity": "sha512-5o3SFTpMYaWrWRlm5qBX84fNDwdiPTbb0qo6KDI+OvIzTaMsEfOJ4vUz+Binxfq0dPen0fU6JLO+xix8Sa8TBA==", 58 | "requires": { 59 | "@firebase/firestore-types": "1.2.1", 60 | "@firebase/logger": "0.1.13", 61 | "@firebase/webchannel-wrapper": "0.2.19", 62 | "grpc": "1.20.0", 63 | "tslib": "1.9.3" 64 | } 65 | }, 66 | "@firebase/firestore-types": { 67 | "version": "1.2.1", 68 | "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-1.2.1.tgz", 69 | "integrity": "sha512-/Klu3uVLoTjW3ckYqFTV3lr9HzEKM7pMpPHao1Sy+YwIUmTjFMI1LE2WcXMx6HN2jipFjjD/Xjg0hY0+0dnPCg==" 70 | }, 71 | "@firebase/functions": { 72 | "version": "0.4.6", 73 | "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.4.6.tgz", 74 | "integrity": "sha512-jpRLY8GyhmFufnN3eilvIuAqD9qsG2/AftGtFaTRL0ObSySmraYcVOpKAxsFZW//9EMNtI9c9/rw+QFq5SkuyA==", 75 | "requires": { 76 | "@firebase/functions-types": "0.3.5", 77 | "@firebase/messaging-types": "0.2.11", 78 | "isomorphic-fetch": "2.2.1", 79 | "tslib": "1.9.3" 80 | } 81 | }, 82 | "@firebase/functions-types": { 83 | "version": "0.3.5", 84 | "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.3.5.tgz", 85 | "integrity": "sha512-3hTMqfSugCfxzT6vZPbzQ58G4941rsFr99fWKXGKFAl2QpdMBCnKmEKdg/p5M47xIPyzIQn6NMF5kCo/eICXhA==" 86 | }, 87 | "@firebase/installations": { 88 | "version": "0.1.0", 89 | "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.1.0.tgz", 90 | "integrity": "sha512-drt9kDT4w/OCXt5nboOIaUGI3lDwHAoSY2V6qJTbtbd3qVSxE0EBLA4c+allpWdmrhGBrASApuA0eAjphxuXIw==", 91 | "requires": { 92 | "@firebase/installations-types": "0.1.0", 93 | "@firebase/util": "0.2.14", 94 | "idb": "3.0.2" 95 | } 96 | }, 97 | "@firebase/installations-types": { 98 | "version": "0.1.0", 99 | "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.1.0.tgz", 100 | "integrity": "sha512-cw2UIvPa3+umy6w7dGj0LqQQ9v7WEOro5s+6B+v54Tw25WyLnR6cBIkyyahv3Uu9QPnGZCIsemlQwxIaIOMb9g==" 101 | }, 102 | "@firebase/logger": { 103 | "version": "0.1.13", 104 | "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.1.13.tgz", 105 | "integrity": "sha512-wIbLwQ2oJCkvHIE7J3FDxpScKY84fSctEEjOi0PB+Yn2dN8AwqtM7YF8rtcY8cxntv8dyR+i7GNg1Nd89cGxkA==" 106 | }, 107 | "@firebase/messaging": { 108 | "version": "0.3.19", 109 | "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.3.19.tgz", 110 | "integrity": "sha512-xY1Hlsj0MqyU/AmJQLyH9Uknhs8+1OsD2xXE8W34qk0g2RtpygUN7JMD21d5w5zZ5dMtLNhVSIxU8oI2rAUjcA==", 111 | "requires": { 112 | "@firebase/messaging-types": "0.2.11", 113 | "@firebase/util": "0.2.14", 114 | "tslib": "1.9.3" 115 | } 116 | }, 117 | "@firebase/messaging-types": { 118 | "version": "0.2.11", 119 | "resolved": "https://registry.npmjs.org/@firebase/messaging-types/-/messaging-types-0.2.11.tgz", 120 | "integrity": "sha512-uWtzPMj1mAX8EbG68SnxE12Waz+hRuO7vtosUFePGBfxVNNmPx5vJyKZTz+hbM4P77XddshAiaEkyduro4gNgA==" 121 | }, 122 | "@firebase/performance": { 123 | "version": "0.2.1", 124 | "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.2.1.tgz", 125 | "integrity": "sha512-vo/24+W35foc2ShRgeIlx2Ej45+Sn6uYPpnYzTtJb3DwE3sb0BVGocVgINbXyguUq2PHS+6yLsCm88y12DS2EA==", 126 | "requires": { 127 | "@firebase/installations": "0.1.0", 128 | "@firebase/logger": "0.1.13", 129 | "@firebase/performance-types": "0.0.1", 130 | "@firebase/util": "0.2.14", 131 | "tslib": "1.9.3" 132 | } 133 | }, 134 | "@firebase/performance-types": { 135 | "version": "0.0.1", 136 | "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.0.1.tgz", 137 | "integrity": "sha512-U45GbVAnPyz7wPLd3FrWdTeaFSvgsnGfGK58VojfEMmFnMAixCM3qBv1XJ0xfhyKbK1xZN4+usWAR8F3CwRAXw==" 138 | }, 139 | "@firebase/polyfill": { 140 | "version": "0.3.13", 141 | "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.3.13.tgz", 142 | "integrity": "sha512-nmz0KMrGZh4wvy8iPnOCtpSXw0LwXPdj9lqgeOVClXMgJBi5+FS1q0W1Ofn7BULmMc8tYsGGY+T2HvfmznMuPg==", 143 | "requires": { 144 | "core-js": "3.0.1", 145 | "promise-polyfill": "8.1.0", 146 | "whatwg-fetch": "2.0.4" 147 | }, 148 | "dependencies": { 149 | "whatwg-fetch": { 150 | "version": "2.0.4", 151 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", 152 | "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" 153 | } 154 | } 155 | }, 156 | "@firebase/storage": { 157 | "version": "0.2.15", 158 | "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.2.15.tgz", 159 | "integrity": "sha512-WR80AXm1btlHERavhSwiTwFAyT/M/Jn6/2I3RAlcVOS6NnKVdRIcSVW1zY9jvO3fdeksqBU9NKTXeXFTmsrw6g==", 160 | "requires": { 161 | "@firebase/storage-types": "0.2.11", 162 | "tslib": "1.9.3" 163 | } 164 | }, 165 | "@firebase/storage-types": { 166 | "version": "0.2.11", 167 | "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.2.11.tgz", 168 | "integrity": "sha512-vGTFJmKbfScmCAVUamREIBTopr5Uusxd8xPAgNDxMZwICvdCjHO0UH0pZZj6iBQuwxLe/NEtFycPnu1kKT+TPw==" 169 | }, 170 | "@firebase/util": { 171 | "version": "0.2.14", 172 | "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.2.14.tgz", 173 | "integrity": "sha512-2ke1Lra0R5T+5ucCMWft/IB2rI/IzumHHYm9aqrM9lJ3XURiWmBHAYrvaPVP7///gDhJAo+NNDUCAJH/Y4PmvA==", 174 | "requires": { 175 | "tslib": "1.9.3" 176 | } 177 | }, 178 | "@firebase/webchannel-wrapper": { 179 | "version": "0.2.19", 180 | "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.19.tgz", 181 | "integrity": "sha512-U9e2dCB38mD2AvV/zAjghauwa0UX15Wt98iBgm8IOw8spluDxysx8UZFUhj38fu0iFXORVRBqseyK2wCxZIl5w==" 182 | }, 183 | "@now/node": { 184 | "version": "1.0.0", 185 | "resolved": "https://registry.npmjs.org/@now/node/-/node-1.0.0.tgz", 186 | "integrity": "sha512-Ha2Dmtf6RHA0wkPjIeEBIBNq5Je0YwUPwUgH3f7FHlnJvTWREncYoUJdfC60BTkOvNi6KukihVLHGcD2wd51Dw==", 187 | "requires": { 188 | "@types/node": "*" 189 | } 190 | }, 191 | "@sentry/cli": { 192 | "version": "1.48.0", 193 | "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.48.0.tgz", 194 | "integrity": "sha512-Td6UpZP6BoI0R5Guctiq3NIphAfskbyeiboWx4H1yuXPA9p4F0hL+Hrz6k3NNfzkt3AQzEkeAhvpKmIfmUOkjA==", 195 | "dev": true, 196 | "requires": { 197 | "fs-copy-file-sync": "^1.1.1", 198 | "https-proxy-agent": "^3.0.0", 199 | "mkdirp": "^0.5.1", 200 | "node-fetch": "^2.1.2", 201 | "progress": "2.0.0", 202 | "proxy-from-env": "^1.0.0" 203 | }, 204 | "dependencies": { 205 | "node-fetch": { 206 | "version": "2.6.0", 207 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", 208 | "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", 209 | "dev": true 210 | } 211 | } 212 | }, 213 | "@sentry/core": { 214 | "version": "5.7.1", 215 | "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.7.1.tgz", 216 | "integrity": "sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg==", 217 | "requires": { 218 | "@sentry/hub": "5.7.1", 219 | "@sentry/minimal": "5.7.1", 220 | "@sentry/types": "5.7.1", 221 | "@sentry/utils": "5.7.1", 222 | "tslib": "^1.9.3" 223 | } 224 | }, 225 | "@sentry/hub": { 226 | "version": "5.7.1", 227 | "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.7.1.tgz", 228 | "integrity": "sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig==", 229 | "requires": { 230 | "@sentry/types": "5.7.1", 231 | "@sentry/utils": "5.7.1", 232 | "tslib": "^1.9.3" 233 | } 234 | }, 235 | "@sentry/minimal": { 236 | "version": "5.7.1", 237 | "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.7.1.tgz", 238 | "integrity": "sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g==", 239 | "requires": { 240 | "@sentry/hub": "5.7.1", 241 | "@sentry/types": "5.7.1", 242 | "tslib": "^1.9.3" 243 | } 244 | }, 245 | "@sentry/node": { 246 | "version": "5.7.1", 247 | "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.7.1.tgz", 248 | "integrity": "sha512-hVM10asFStrOhYZzMqFM7V1lrHkr1ydc2n/SFG0ZmIQxfTjCVElyXV/BJASIdqadM1fFIvvtD/EfgkTcZmub1g==", 249 | "requires": { 250 | "@sentry/core": "5.7.1", 251 | "@sentry/hub": "5.7.1", 252 | "@sentry/types": "5.7.1", 253 | "@sentry/utils": "5.7.1", 254 | "cookie": "^0.3.1", 255 | "https-proxy-agent": "^3.0.0", 256 | "lru_map": "^0.3.3", 257 | "tslib": "^1.9.3" 258 | } 259 | }, 260 | "@sentry/types": { 261 | "version": "5.7.1", 262 | "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.7.1.tgz", 263 | "integrity": "sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ==" 264 | }, 265 | "@sentry/utils": { 266 | "version": "5.7.1", 267 | "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.7.1.tgz", 268 | "integrity": "sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q==", 269 | "requires": { 270 | "@sentry/types": "5.7.1", 271 | "tslib": "^1.9.3" 272 | } 273 | }, 274 | "@types/http-proxy": { 275 | "version": "1.17.0", 276 | "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.0.tgz", 277 | "integrity": "sha512-l+s0IoxSHqhLFJPDHRfO235kgrCkvFD8JmdV/T9C4BKBYPIjrQopGFH4r7h2e3jQqgJRCthRCAZIxDoFnj1zwQ==", 278 | "requires": { 279 | "@types/node": "*" 280 | } 281 | }, 282 | "@types/node": { 283 | "version": "12.7.5", 284 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", 285 | "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==" 286 | }, 287 | "@types/uuid": { 288 | "version": "3.4.5", 289 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", 290 | "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", 291 | "requires": { 292 | "@types/node": "*" 293 | } 294 | }, 295 | "agent-base": { 296 | "version": "4.3.0", 297 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", 298 | "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", 299 | "requires": { 300 | "es6-promisify": "^5.0.0" 301 | } 302 | }, 303 | "ansi-regex": { 304 | "version": "2.1.1", 305 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 306 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 307 | }, 308 | "ascli": { 309 | "version": "1.0.1", 310 | "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", 311 | "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", 312 | "requires": { 313 | "colour": "~0.7.1", 314 | "optjs": "~3.2.2" 315 | } 316 | }, 317 | "balanced-match": { 318 | "version": "1.0.0", 319 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 320 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 321 | }, 322 | "brace-expansion": { 323 | "version": "1.1.11", 324 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 325 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 326 | "requires": { 327 | "balanced-match": "^1.0.0", 328 | "concat-map": "0.0.1" 329 | } 330 | }, 331 | "bytebuffer": { 332 | "version": "5.0.1", 333 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", 334 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", 335 | "requires": { 336 | "long": "~3" 337 | } 338 | }, 339 | "camelcase": { 340 | "version": "2.1.1", 341 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 342 | "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" 343 | }, 344 | "cliui": { 345 | "version": "3.2.0", 346 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", 347 | "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", 348 | "requires": { 349 | "string-width": "^1.0.1", 350 | "strip-ansi": "^3.0.1", 351 | "wrap-ansi": "^2.0.0" 352 | } 353 | }, 354 | "code-point-at": { 355 | "version": "1.1.0", 356 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 357 | "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 358 | }, 359 | "colour": { 360 | "version": "0.7.1", 361 | "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", 362 | "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=" 363 | }, 364 | "concat-map": { 365 | "version": "0.0.1", 366 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 367 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 368 | }, 369 | "cookie": { 370 | "version": "0.3.1", 371 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 372 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 373 | }, 374 | "core-js": { 375 | "version": "3.0.1", 376 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.1.tgz", 377 | "integrity": "sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew==" 378 | }, 379 | "debug": { 380 | "version": "3.2.6", 381 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 382 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 383 | "requires": { 384 | "ms": "^2.1.1" 385 | } 386 | }, 387 | "decamelize": { 388 | "version": "1.2.0", 389 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 390 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 391 | }, 392 | "dom-storage": { 393 | "version": "2.1.0", 394 | "resolved": "https://registry.npmjs.org/dom-storage/-/dom-storage-2.1.0.tgz", 395 | "integrity": "sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q==" 396 | }, 397 | "encoding": { 398 | "version": "0.1.12", 399 | "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", 400 | "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", 401 | "requires": { 402 | "iconv-lite": "~0.4.13" 403 | } 404 | }, 405 | "es6-promise": { 406 | "version": "4.2.8", 407 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 408 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" 409 | }, 410 | "es6-promisify": { 411 | "version": "5.0.0", 412 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 413 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 414 | "requires": { 415 | "es6-promise": "^4.0.3" 416 | } 417 | }, 418 | "eventemitter3": { 419 | "version": "4.0.0", 420 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", 421 | "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" 422 | }, 423 | "faye-websocket": { 424 | "version": "0.11.1", 425 | "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", 426 | "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", 427 | "requires": { 428 | "websocket-driver": ">=0.5.1" 429 | } 430 | }, 431 | "firebase": { 432 | "version": "5.11.1", 433 | "resolved": "https://registry.npmjs.org/firebase/-/firebase-5.11.1.tgz", 434 | "integrity": "sha512-cop2UHytKas8WJZTovZqhpZIgwRfsvegijyOjgmMJoaOHCnyH4eymPneglgXsK5ExOdxJSTC4QD5qETrdL3dMw==", 435 | "requires": { 436 | "@firebase/app": "0.3.17", 437 | "@firebase/auth": "0.10.2", 438 | "@firebase/database": "0.3.20", 439 | "@firebase/firestore": "1.2.2", 440 | "@firebase/functions": "0.4.6", 441 | "@firebase/messaging": "0.3.19", 442 | "@firebase/performance": "0.2.1", 443 | "@firebase/polyfill": "0.3.13", 444 | "@firebase/storage": "0.2.15" 445 | } 446 | }, 447 | "follow-redirects": { 448 | "version": "1.9.0", 449 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", 450 | "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", 451 | "requires": { 452 | "debug": "^3.0.0" 453 | } 454 | }, 455 | "fs-copy-file-sync": { 456 | "version": "1.1.1", 457 | "resolved": "https://registry.npmjs.org/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz", 458 | "integrity": "sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ==", 459 | "dev": true 460 | }, 461 | "fs.realpath": { 462 | "version": "1.0.0", 463 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 464 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 465 | }, 466 | "glob": { 467 | "version": "7.1.4", 468 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", 469 | "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", 470 | "requires": { 471 | "fs.realpath": "^1.0.0", 472 | "inflight": "^1.0.4", 473 | "inherits": "2", 474 | "minimatch": "^3.0.4", 475 | "once": "^1.3.0", 476 | "path-is-absolute": "^1.0.0" 477 | } 478 | }, 479 | "grpc": { 480 | "version": "1.20.0", 481 | "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.20.0.tgz", 482 | "integrity": "sha512-HgYuJzRomkBlJAfC/78epuWzwMiByxgj4JsO6G6dHXXNfARTsUqpM/FmPSJJNFGvzCev0g6tn33CE7nWEmhDEg==", 483 | "requires": { 484 | "lodash.camelcase": "^4.3.0", 485 | "lodash.clone": "^4.5.0", 486 | "nan": "^2.0.0", 487 | "node-pre-gyp": "^0.12.0", 488 | "protobufjs": "^5.0.3" 489 | }, 490 | "dependencies": { 491 | "abbrev": { 492 | "version": "1.1.1", 493 | "bundled": true 494 | }, 495 | "ansi-regex": { 496 | "version": "2.1.1", 497 | "bundled": true 498 | }, 499 | "aproba": { 500 | "version": "1.2.0", 501 | "bundled": true 502 | }, 503 | "are-we-there-yet": { 504 | "version": "1.1.5", 505 | "bundled": true, 506 | "requires": { 507 | "delegates": "^1.0.0", 508 | "readable-stream": "^2.0.6" 509 | } 510 | }, 511 | "balanced-match": { 512 | "version": "1.0.0", 513 | "bundled": true 514 | }, 515 | "brace-expansion": { 516 | "version": "1.1.11", 517 | "bundled": true, 518 | "requires": { 519 | "balanced-match": "^1.0.0", 520 | "concat-map": "0.0.1" 521 | } 522 | }, 523 | "chownr": { 524 | "version": "1.1.1", 525 | "bundled": true 526 | }, 527 | "code-point-at": { 528 | "version": "1.1.0", 529 | "bundled": true 530 | }, 531 | "concat-map": { 532 | "version": "0.0.1", 533 | "bundled": true 534 | }, 535 | "console-control-strings": { 536 | "version": "1.1.0", 537 | "bundled": true 538 | }, 539 | "core-util-is": { 540 | "version": "1.0.2", 541 | "bundled": true 542 | }, 543 | "debug": { 544 | "version": "2.6.9", 545 | "bundled": true, 546 | "requires": { 547 | "ms": "2.0.0" 548 | } 549 | }, 550 | "deep-extend": { 551 | "version": "0.6.0", 552 | "bundled": true 553 | }, 554 | "delegates": { 555 | "version": "1.0.0", 556 | "bundled": true 557 | }, 558 | "detect-libc": { 559 | "version": "1.0.3", 560 | "bundled": true 561 | }, 562 | "fs-minipass": { 563 | "version": "1.2.5", 564 | "bundled": true, 565 | "requires": { 566 | "minipass": "^2.2.1" 567 | } 568 | }, 569 | "fs.realpath": { 570 | "version": "1.0.0", 571 | "bundled": true 572 | }, 573 | "gauge": { 574 | "version": "2.7.4", 575 | "bundled": true, 576 | "requires": { 577 | "aproba": "^1.0.3", 578 | "console-control-strings": "^1.0.0", 579 | "has-unicode": "^2.0.0", 580 | "object-assign": "^4.1.0", 581 | "signal-exit": "^3.0.0", 582 | "string-width": "^1.0.1", 583 | "strip-ansi": "^3.0.1", 584 | "wide-align": "^1.1.0" 585 | } 586 | }, 587 | "glob": { 588 | "version": "7.1.2", 589 | "bundled": true, 590 | "requires": { 591 | "fs.realpath": "^1.0.0", 592 | "inflight": "^1.0.4", 593 | "inherits": "2", 594 | "minimatch": "^3.0.4", 595 | "once": "^1.3.0", 596 | "path-is-absolute": "^1.0.0" 597 | } 598 | }, 599 | "has-unicode": { 600 | "version": "2.0.1", 601 | "bundled": true 602 | }, 603 | "iconv-lite": { 604 | "version": "0.4.23", 605 | "bundled": true, 606 | "requires": { 607 | "safer-buffer": ">= 2.1.2 < 3" 608 | } 609 | }, 610 | "ignore-walk": { 611 | "version": "3.0.1", 612 | "bundled": true, 613 | "requires": { 614 | "minimatch": "^3.0.4" 615 | } 616 | }, 617 | "inflight": { 618 | "version": "1.0.6", 619 | "bundled": true, 620 | "requires": { 621 | "once": "^1.3.0", 622 | "wrappy": "1" 623 | } 624 | }, 625 | "inherits": { 626 | "version": "2.0.3", 627 | "bundled": true 628 | }, 629 | "ini": { 630 | "version": "1.3.5", 631 | "bundled": true 632 | }, 633 | "is-fullwidth-code-point": { 634 | "version": "1.0.0", 635 | "bundled": true, 636 | "requires": { 637 | "number-is-nan": "^1.0.0" 638 | } 639 | }, 640 | "isarray": { 641 | "version": "1.0.0", 642 | "bundled": true 643 | }, 644 | "minimatch": { 645 | "version": "3.0.4", 646 | "bundled": true, 647 | "requires": { 648 | "brace-expansion": "^1.1.7" 649 | } 650 | }, 651 | "minimist": { 652 | "version": "1.2.0", 653 | "bundled": true 654 | }, 655 | "minipass": { 656 | "version": "2.3.5", 657 | "bundled": true, 658 | "requires": { 659 | "safe-buffer": "^5.1.2", 660 | "yallist": "^3.0.0" 661 | } 662 | }, 663 | "minizlib": { 664 | "version": "1.1.1", 665 | "bundled": true, 666 | "requires": { 667 | "minipass": "^2.2.1" 668 | } 669 | }, 670 | "mkdirp": { 671 | "version": "0.5.1", 672 | "bundled": true, 673 | "requires": { 674 | "minimist": "0.0.8" 675 | }, 676 | "dependencies": { 677 | "minimist": { 678 | "version": "0.0.8", 679 | "bundled": true 680 | } 681 | } 682 | }, 683 | "ms": { 684 | "version": "2.0.0", 685 | "bundled": true 686 | }, 687 | "needle": { 688 | "version": "2.2.4", 689 | "bundled": true, 690 | "requires": { 691 | "debug": "^2.1.2", 692 | "iconv-lite": "^0.4.4", 693 | "sax": "^1.2.4" 694 | } 695 | }, 696 | "node-pre-gyp": { 697 | "version": "0.12.0", 698 | "bundled": true, 699 | "requires": { 700 | "detect-libc": "^1.0.2", 701 | "mkdirp": "^0.5.1", 702 | "needle": "^2.2.1", 703 | "nopt": "^4.0.1", 704 | "npm-packlist": "^1.1.6", 705 | "npmlog": "^4.0.2", 706 | "rc": "^1.2.7", 707 | "rimraf": "^2.6.1", 708 | "semver": "^5.3.0", 709 | "tar": "^4" 710 | } 711 | }, 712 | "nopt": { 713 | "version": "4.0.1", 714 | "bundled": true, 715 | "requires": { 716 | "abbrev": "1", 717 | "osenv": "^0.1.4" 718 | } 719 | }, 720 | "npm-bundled": { 721 | "version": "1.0.5", 722 | "bundled": true 723 | }, 724 | "npm-packlist": { 725 | "version": "1.1.12", 726 | "bundled": true, 727 | "requires": { 728 | "ignore-walk": "^3.0.1", 729 | "npm-bundled": "^1.0.1" 730 | } 731 | }, 732 | "npmlog": { 733 | "version": "4.1.2", 734 | "bundled": true, 735 | "requires": { 736 | "are-we-there-yet": "~1.1.2", 737 | "console-control-strings": "~1.1.0", 738 | "gauge": "~2.7.3", 739 | "set-blocking": "~2.0.0" 740 | } 741 | }, 742 | "number-is-nan": { 743 | "version": "1.0.1", 744 | "bundled": true 745 | }, 746 | "object-assign": { 747 | "version": "4.1.1", 748 | "bundled": true 749 | }, 750 | "once": { 751 | "version": "1.4.0", 752 | "bundled": true, 753 | "requires": { 754 | "wrappy": "1" 755 | } 756 | }, 757 | "os-homedir": { 758 | "version": "1.0.2", 759 | "bundled": true 760 | }, 761 | "os-tmpdir": { 762 | "version": "1.0.2", 763 | "bundled": true 764 | }, 765 | "osenv": { 766 | "version": "0.1.5", 767 | "bundled": true, 768 | "requires": { 769 | "os-homedir": "^1.0.0", 770 | "os-tmpdir": "^1.0.0" 771 | } 772 | }, 773 | "path-is-absolute": { 774 | "version": "1.0.1", 775 | "bundled": true 776 | }, 777 | "process-nextick-args": { 778 | "version": "2.0.0", 779 | "bundled": true 780 | }, 781 | "rc": { 782 | "version": "1.2.8", 783 | "bundled": true, 784 | "requires": { 785 | "deep-extend": "^0.6.0", 786 | "ini": "~1.3.0", 787 | "minimist": "^1.2.0", 788 | "strip-json-comments": "~2.0.1" 789 | } 790 | }, 791 | "readable-stream": { 792 | "version": "2.3.6", 793 | "bundled": true, 794 | "requires": { 795 | "core-util-is": "~1.0.0", 796 | "inherits": "~2.0.3", 797 | "isarray": "~1.0.0", 798 | "process-nextick-args": "~2.0.0", 799 | "safe-buffer": "~5.1.1", 800 | "string_decoder": "~1.1.1", 801 | "util-deprecate": "~1.0.1" 802 | } 803 | }, 804 | "rimraf": { 805 | "version": "2.6.2", 806 | "bundled": true, 807 | "requires": { 808 | "glob": "^7.0.5" 809 | } 810 | }, 811 | "safe-buffer": { 812 | "version": "5.1.2", 813 | "bundled": true 814 | }, 815 | "safer-buffer": { 816 | "version": "2.1.2", 817 | "bundled": true 818 | }, 819 | "sax": { 820 | "version": "1.2.4", 821 | "bundled": true 822 | }, 823 | "semver": { 824 | "version": "5.6.0", 825 | "bundled": true 826 | }, 827 | "set-blocking": { 828 | "version": "2.0.0", 829 | "bundled": true 830 | }, 831 | "signal-exit": { 832 | "version": "3.0.2", 833 | "bundled": true 834 | }, 835 | "string-width": { 836 | "version": "1.0.2", 837 | "bundled": true, 838 | "requires": { 839 | "code-point-at": "^1.0.0", 840 | "is-fullwidth-code-point": "^1.0.0", 841 | "strip-ansi": "^3.0.0" 842 | } 843 | }, 844 | "string_decoder": { 845 | "version": "1.1.1", 846 | "bundled": true, 847 | "requires": { 848 | "safe-buffer": "~5.1.0" 849 | } 850 | }, 851 | "strip-ansi": { 852 | "version": "3.0.1", 853 | "bundled": true, 854 | "requires": { 855 | "ansi-regex": "^2.0.0" 856 | } 857 | }, 858 | "strip-json-comments": { 859 | "version": "2.0.1", 860 | "bundled": true 861 | }, 862 | "tar": { 863 | "version": "4.4.8", 864 | "bundled": true, 865 | "requires": { 866 | "chownr": "^1.1.1", 867 | "fs-minipass": "^1.2.5", 868 | "minipass": "^2.3.4", 869 | "minizlib": "^1.1.1", 870 | "mkdirp": "^0.5.0", 871 | "safe-buffer": "^5.1.2", 872 | "yallist": "^3.0.2" 873 | } 874 | }, 875 | "util-deprecate": { 876 | "version": "1.0.2", 877 | "bundled": true 878 | }, 879 | "wide-align": { 880 | "version": "1.1.3", 881 | "bundled": true, 882 | "requires": { 883 | "string-width": "^1.0.2 || 2" 884 | } 885 | }, 886 | "wrappy": { 887 | "version": "1.0.2", 888 | "bundled": true 889 | }, 890 | "yallist": { 891 | "version": "3.0.3", 892 | "bundled": true 893 | } 894 | } 895 | }, 896 | "http-parser-js": { 897 | "version": "0.4.10", 898 | "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", 899 | "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=" 900 | }, 901 | "http-proxy": { 902 | "version": "1.18.0", 903 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", 904 | "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", 905 | "requires": { 906 | "eventemitter3": "^4.0.0", 907 | "follow-redirects": "^1.0.0", 908 | "requires-port": "^1.0.0" 909 | } 910 | }, 911 | "https-proxy-agent": { 912 | "version": "3.0.0", 913 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz", 914 | "integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==", 915 | "requires": { 916 | "agent-base": "^4.3.0", 917 | "debug": "^3.1.0" 918 | } 919 | }, 920 | "iconv-lite": { 921 | "version": "0.4.24", 922 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 923 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 924 | "requires": { 925 | "safer-buffer": ">= 2.1.2 < 3" 926 | } 927 | }, 928 | "idb": { 929 | "version": "3.0.2", 930 | "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", 931 | "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" 932 | }, 933 | "inflight": { 934 | "version": "1.0.6", 935 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 936 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 937 | "requires": { 938 | "once": "^1.3.0", 939 | "wrappy": "1" 940 | } 941 | }, 942 | "inherits": { 943 | "version": "2.0.4", 944 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 945 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 946 | }, 947 | "invert-kv": { 948 | "version": "1.0.0", 949 | "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", 950 | "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" 951 | }, 952 | "is-fullwidth-code-point": { 953 | "version": "1.0.0", 954 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 955 | "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 956 | "requires": { 957 | "number-is-nan": "^1.0.0" 958 | } 959 | }, 960 | "is-stream": { 961 | "version": "1.1.0", 962 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 963 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 964 | }, 965 | "isomorphic-fetch": { 966 | "version": "2.2.1", 967 | "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", 968 | "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", 969 | "requires": { 970 | "node-fetch": "^1.0.1", 971 | "whatwg-fetch": ">=0.10.0" 972 | } 973 | }, 974 | "lcid": { 975 | "version": "1.0.0", 976 | "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", 977 | "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", 978 | "requires": { 979 | "invert-kv": "^1.0.0" 980 | } 981 | }, 982 | "lodash.camelcase": { 983 | "version": "4.3.0", 984 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 985 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 986 | }, 987 | "lodash.clone": { 988 | "version": "4.5.0", 989 | "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", 990 | "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" 991 | }, 992 | "long": { 993 | "version": "3.2.0", 994 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", 995 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" 996 | }, 997 | "lru_map": { 998 | "version": "0.3.3", 999 | "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", 1000 | "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" 1001 | }, 1002 | "minimatch": { 1003 | "version": "3.0.4", 1004 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1005 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 1006 | "requires": { 1007 | "brace-expansion": "^1.1.7" 1008 | } 1009 | }, 1010 | "minimist": { 1011 | "version": "0.0.8", 1012 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 1013 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 1014 | "dev": true 1015 | }, 1016 | "mkdirp": { 1017 | "version": "0.5.1", 1018 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 1019 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 1020 | "dev": true, 1021 | "requires": { 1022 | "minimist": "0.0.8" 1023 | } 1024 | }, 1025 | "ms": { 1026 | "version": "2.1.2", 1027 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1028 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 1029 | }, 1030 | "nan": { 1031 | "version": "2.14.0", 1032 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", 1033 | "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" 1034 | }, 1035 | "node-fetch": { 1036 | "version": "1.7.3", 1037 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", 1038 | "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", 1039 | "requires": { 1040 | "encoding": "^0.1.11", 1041 | "is-stream": "^1.0.1" 1042 | } 1043 | }, 1044 | "number-is-nan": { 1045 | "version": "1.0.1", 1046 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 1047 | "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 1048 | }, 1049 | "once": { 1050 | "version": "1.4.0", 1051 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1052 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1053 | "requires": { 1054 | "wrappy": "1" 1055 | } 1056 | }, 1057 | "optjs": { 1058 | "version": "3.2.2", 1059 | "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", 1060 | "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=" 1061 | }, 1062 | "os-locale": { 1063 | "version": "1.4.0", 1064 | "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", 1065 | "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", 1066 | "requires": { 1067 | "lcid": "^1.0.0" 1068 | } 1069 | }, 1070 | "path-is-absolute": { 1071 | "version": "1.0.1", 1072 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1073 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 1074 | }, 1075 | "progress": { 1076 | "version": "2.0.0", 1077 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", 1078 | "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", 1079 | "dev": true 1080 | }, 1081 | "promise-polyfill": { 1082 | "version": "8.1.0", 1083 | "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.1.0.tgz", 1084 | "integrity": "sha512-OzSf6gcCUQ01byV4BgwyUCswlaQQ6gzXc23aLQWhicvfX9kfsUiUhgt3CCQej8jDnl8/PhGF31JdHX2/MzF3WA==" 1085 | }, 1086 | "protobufjs": { 1087 | "version": "5.0.3", 1088 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz", 1089 | "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==", 1090 | "requires": { 1091 | "ascli": "~1", 1092 | "bytebuffer": "~5", 1093 | "glob": "^7.0.5", 1094 | "yargs": "^3.10.0" 1095 | } 1096 | }, 1097 | "proxy-from-env": { 1098 | "version": "1.0.0", 1099 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", 1100 | "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", 1101 | "dev": true 1102 | }, 1103 | "requires-port": { 1104 | "version": "1.0.0", 1105 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", 1106 | "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" 1107 | }, 1108 | "safe-buffer": { 1109 | "version": "5.2.0", 1110 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", 1111 | "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" 1112 | }, 1113 | "safer-buffer": { 1114 | "version": "2.1.2", 1115 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1116 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1117 | }, 1118 | "slack": { 1119 | "version": "11.0.2", 1120 | "resolved": "https://registry.npmjs.org/slack/-/slack-11.0.2.tgz", 1121 | "integrity": "sha512-rv842+S+AGyZCmMMd8xPtW5DvJ9LzWTAKfxi8Gw57oYlXgcKtFuHd4nqk6lTPpRKdUGn3tx/Drd0rjQR3dQPqw==", 1122 | "requires": { 1123 | "tiny-json-http": "^7.0.2" 1124 | } 1125 | }, 1126 | "string-width": { 1127 | "version": "1.0.2", 1128 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1129 | "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1130 | "requires": { 1131 | "code-point-at": "^1.0.0", 1132 | "is-fullwidth-code-point": "^1.0.0", 1133 | "strip-ansi": "^3.0.0" 1134 | } 1135 | }, 1136 | "strip-ansi": { 1137 | "version": "3.0.1", 1138 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1139 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1140 | "requires": { 1141 | "ansi-regex": "^2.0.0" 1142 | } 1143 | }, 1144 | "tiny-json-http": { 1145 | "version": "7.1.2", 1146 | "resolved": "https://registry.npmjs.org/tiny-json-http/-/tiny-json-http-7.1.2.tgz", 1147 | "integrity": "sha512-XB9Bu+ohdQso6ziPFNVqK+pcTt0l8BSRkW/CCBq0pUVlLxcYDsorpo7ae5yPhu2CF1xYgJuKVLF7cfOGeLCTlA==" 1148 | }, 1149 | "tslib": { 1150 | "version": "1.9.3", 1151 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", 1152 | "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" 1153 | }, 1154 | "typescript": { 1155 | "version": "3.6.4", 1156 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", 1157 | "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==" 1158 | }, 1159 | "uuid": { 1160 | "version": "3.3.3", 1161 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", 1162 | "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" 1163 | }, 1164 | "websocket-driver": { 1165 | "version": "0.7.3", 1166 | "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", 1167 | "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", 1168 | "requires": { 1169 | "http-parser-js": ">=0.4.0 <0.4.11", 1170 | "safe-buffer": ">=5.1.0", 1171 | "websocket-extensions": ">=0.1.1" 1172 | } 1173 | }, 1174 | "websocket-extensions": { 1175 | "version": "0.1.4", 1176 | "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", 1177 | "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" 1178 | }, 1179 | "whatwg-fetch": { 1180 | "version": "3.0.0", 1181 | "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", 1182 | "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" 1183 | }, 1184 | "window-size": { 1185 | "version": "0.1.4", 1186 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", 1187 | "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" 1188 | }, 1189 | "wrap-ansi": { 1190 | "version": "2.1.0", 1191 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", 1192 | "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", 1193 | "requires": { 1194 | "string-width": "^1.0.1", 1195 | "strip-ansi": "^3.0.1" 1196 | } 1197 | }, 1198 | "wrappy": { 1199 | "version": "1.0.2", 1200 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1201 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1202 | }, 1203 | "xmlhttprequest": { 1204 | "version": "1.8.0", 1205 | "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", 1206 | "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" 1207 | }, 1208 | "y18n": { 1209 | "version": "3.2.1", 1210 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", 1211 | "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" 1212 | }, 1213 | "yargs": { 1214 | "version": "3.32.0", 1215 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", 1216 | "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", 1217 | "requires": { 1218 | "camelcase": "^2.0.1", 1219 | "cliui": "^3.0.3", 1220 | "decamelize": "^1.1.1", 1221 | "os-locale": "^1.4.0", 1222 | "string-width": "^1.0.1", 1223 | "window-size": "^0.1.4", 1224 | "y18n": "^3.2.0" 1225 | } 1226 | } 1227 | } 1228 | } 1229 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-code-review-notifier", 3 | "version": "0.0.1", 4 | "description": "", 5 | "repository": { 6 | "url": "https://github.com/EnixCoda/github-code-review-notifier", 7 | "type": "git" 8 | }, 9 | "main": "build/index.js", 10 | "author": "EnixCoda", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@now/node": "^1.0.0", 14 | "@sentry/node": "^5.7.1", 15 | "@types/http-proxy": "^1.17.0", 16 | "@types/uuid": "^3.4.5", 17 | "firebase": "^8.4.2", 18 | "http-proxy": "^1.17.0", 19 | "slack": "^11.0.2", 20 | "typescript": "^3.6.4", 21 | "uuid": "^3.3.2" 22 | }, 23 | "devDependencies": { 24 | "@sentry/cli": "^1.48.0" 25 | }, 26 | "prettier": { 27 | "printWidth": 100, 28 | "semi": false, 29 | "singleQuote": true, 30 | "trailingComma": "all" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/bot.ts: -------------------------------------------------------------------------------- 1 | import * as Slack from 'slack' 2 | import uuid from 'uuid/v4' 3 | import { paths } from '../api/paths' 4 | import { getURL, RouteHandler } from './' 5 | import { clientID, clientSecret, verificationToken } from './config' 6 | import * as db from './db' 7 | import { githubUserPageLink, mention, slackLink } from './format' 8 | 9 | function generateWebhookURL(host: string, workspace: string) { 10 | return `https://${host}${paths.GitHub}?workspace=${workspace}` 11 | } 12 | 13 | const handleChallenge: RouteHandler = (req, data) => { 14 | if (data.token !== verificationToken) throw Error('wrong verification token') 15 | return { challenge: data.challenge } 16 | } 17 | 18 | const messageTypes = { 19 | botMessage: 'bot_message', 20 | appMention: 'app_mention', 21 | } 22 | 23 | export const actions = { 24 | getWebhook: `get-webhook`, 25 | link: `link`, 26 | linkOtherUser: `link-other-user`, 27 | unlink: `unlink`, 28 | feedback: `feedback`, 29 | } 30 | 31 | export async function botSpeak( 32 | workspace: string, 33 | channel: string, 34 | text: string, 35 | extra?: Partial, 36 | ) { 37 | const { botToken } = await db.loadWorkspace(workspace) 38 | return Slack.chat 39 | .postMessage(Object.assign({ as_user: true, token: botToken, channel, text }, extra)) 40 | .then( 41 | ({ ok }) => ok, 42 | err => { 43 | if (err === 'account_inactive' || err.message === 'account_inactive') return false 44 | throw err 45 | }, 46 | ) 47 | } 48 | 49 | const mainMenuActions = [ 50 | { 51 | text: 'Link you to GitHub', 52 | type: 'button', 53 | name: actions.link, 54 | value: JSON.stringify(null), 55 | }, 56 | { 57 | text: 'Undo link', 58 | type: 'button', 59 | name: actions.unlink, 60 | value: JSON.stringify(null), 61 | }, 62 | { 63 | text: 'Setup GitHub projects', 64 | type: 'button', 65 | name: actions.getWebhook, 66 | value: JSON.stringify(null), 67 | }, 68 | { 69 | text: 'Feedback', 70 | type: 'button', 71 | name: actions.feedback, 72 | value: JSON.stringify(null), 73 | }, 74 | ] 75 | 76 | export const handleBotMessages: RouteHandler = async (req, data) => { 77 | switch (data.type) { 78 | case 'url_verification': 79 | return handleChallenge(req, data) 80 | } 81 | 82 | switch (data.event.subtype) { 83 | case messageTypes.botMessage: 84 | // ignore bot message 85 | return 86 | } 87 | 88 | const workspace = data.team_id 89 | const { botID } = await db.loadWorkspace(workspace) 90 | 91 | if (botID === data.event.user) { 92 | // ignore message from this app 93 | return 94 | } 95 | 96 | if (!data.event.user) { 97 | // might be beautified message, ignore 98 | return 99 | } 100 | 101 | await botSpeak(workspace, data.event.channel, '', { 102 | attachments: [ 103 | { 104 | text: 'Hi, I can do these for you, check them out!', 105 | fallback: 'Something went wrong.', 106 | callback_id: 'main_menu_callback_id', 107 | color: '#3AA3E3', 108 | attachment_type: 'default', 109 | actions: mainMenuActions, 110 | }, 111 | ], 112 | }) 113 | } 114 | 115 | const interactiveMessageTypes = { 116 | dialogSubmission: `dialog_submission`, 117 | interactiveMessage: `interactive_message`, 118 | } 119 | 120 | export const handleInteractiveComponents: RouteHandler = async function handleInteractiveComponents( 121 | req, 122 | data, 123 | ) { 124 | // either user clicked button, or confirmed dialog 125 | 126 | if (data.payload) { 127 | const payload = JSON.parse(decodeURIComponent(data.payload)) 128 | const { 129 | team: { id: workspace }, 130 | user: { id: slackUserID }, 131 | channel: { id: channelID }, 132 | type: interactiveMessageType, 133 | } = payload 134 | try { 135 | switch (interactiveMessageType) { 136 | case interactiveMessageTypes.interactiveMessage: { 137 | // Slack doc says there would be only one action: 138 | // https://api.slack.com/docs/interactive-message-field-guide "Action URL invocation payload" 139 | const { 140 | actions: [{ name: action, value }], 141 | } = payload 142 | 143 | const state = createInitialState(action, value) 144 | 145 | switch (action) { 146 | case actions.feedback: { 147 | await botSpeak( 148 | workspace, 149 | channelID, 150 | `📝 If you have any question, feature request or bug report, please ${slackLink( 151 | 'https://github.com/EnixCoda/github-code-review-notifier/issues/new', 152 | 'draft an issue', 153 | )} or email the author enixcoda@gmail.com.`, 154 | ) 155 | return 156 | } 157 | case actions.getWebhook: { 158 | const url = getURL(req) 159 | const webhook = generateWebhookURL(url.host, workspace) 160 | await botSpeak( 161 | workspace, 162 | channelID, 163 | `🔧 Please setup your GitHub projects with this webhook:\n${webhook}\n\nNeed help? Read the ${slackLink( 164 | 'https://enixcoda.github.io/github-code-review-notifier/#connect-github-projects', 165 | 'connect GitHub projects', 166 | )} section.`, 167 | ) 168 | return 169 | } 170 | case actions.link: 171 | case actions.unlink: { 172 | const links = await db.loadLinks(workspace, { 173 | slack: slackUserID, 174 | }) 175 | const githubNames = links ? links.map(({ github }) => github) : null 176 | 177 | if (action === actions.unlink) { 178 | if (!githubNames) { 179 | await botSpeak( 180 | workspace, 181 | channelID, 182 | `👻 Hi ${mention( 183 | slackUserID, 184 | )}, you are not linked to any GitHub users yet. You can get started by clicking "Link to GitHub" on the left.`, 185 | ) 186 | } else { 187 | await openUnlinkDialog(workspace, payload, githubNames) 188 | } 189 | } else { 190 | await openLinkDialog(workspace, payload, githubNames || undefined, state) 191 | } 192 | return 193 | } 194 | case actions.linkOtherUser: { 195 | const { githubName } = state 196 | if (typeof githubName !== 'string') throw new Error('Unexpected state') 197 | 198 | await openLinkForOtherDialog(workspace, payload, githubName, { 199 | githubName, 200 | }) 201 | 202 | return 203 | } 204 | default: 205 | break 206 | } 207 | return 208 | } 209 | case interactiveMessageTypes.dialogSubmission: { 210 | const { submission = {} } = payload 211 | const { action, state } = parseState(payload.state) 212 | 213 | const githubName = 214 | (state && state.githubName) || submission.githubName || submission.github_name 215 | const targetSlackUserID = 216 | (state && state.slackUserID) || 217 | (submission.slackUser && submission.slackUser.id) || 218 | slackUserID 219 | 220 | let responseMessage: string 221 | switch (action) { 222 | case actions.linkOtherUser: 223 | case actions.link: { 224 | // TODO: prevent duplication 225 | try { 226 | await db.saveLink(workspace, { 227 | github: githubName, 228 | slack: targetSlackUserID, 229 | }) 230 | responseMessage = `🤝 Linked ${mention(targetSlackUserID)} to ${githubUserPageLink( 231 | githubName, 232 | )}!` 233 | } catch (err) { 234 | console.error(err) 235 | responseMessage = `Oops, link failed. You may try again later.` 236 | } 237 | break 238 | } 239 | case actions.unlink: { 240 | try { 241 | await db.removeLink(workspace, { github: githubName }) 242 | responseMessage = `👋 Unlinked ${mention( 243 | targetSlackUserID, 244 | )} from ${githubUserPageLink(githubName)}!` 245 | } catch (err) { 246 | console.error(err) 247 | responseMessage = `Sorry, unlink failed.` 248 | } 249 | break 250 | } 251 | default: 252 | throw `unknown command ${action}` 253 | } 254 | 255 | await botSpeak(workspace, channelID, responseMessage) 256 | 257 | return 258 | } 259 | default: 260 | console.log(`unknown payload type`, data.payload) 261 | } 262 | } catch (err) { 263 | console.error(err) 264 | 265 | await botSpeak(workspace, channelID, `Oops, something went wrong :(`) 266 | 267 | throw err 268 | } 269 | } else { 270 | console.log(`no payload detected`) 271 | } 272 | } 273 | 274 | function createInitialState(name: string, value: string) { 275 | const state: { 276 | [key: string]: ExpectedAny 277 | } = {} 278 | if (Object.values(actions).includes(value)) { 279 | // legacy message, leave state empty 280 | } else { 281 | // might be latest JSON serialized message 282 | try { 283 | Object.assign(state, JSON.parse(value)) 284 | } catch (err) { 285 | throw new Error(`Unknown interactive message: ${JSON.stringify({ name, value })}`) 286 | } 287 | } 288 | return state 289 | } 290 | 291 | function parseState(state: string) { 292 | if (Object.values(actions).includes(state)) { 293 | // legacy mode 294 | return { 295 | action: state, 296 | } 297 | } else { 298 | try { 299 | return JSON.parse(state) 300 | } catch (err) { 301 | throw new Error(`Unknown interactive submission state: ${state}`) 302 | } 303 | } 304 | } 305 | 306 | async function openSlackDialog( 307 | workspace: string, 308 | payload: SlackPayload, 309 | action: string, 310 | state: ExpectedAny, 311 | title: string, 312 | elements: SlackElement, 313 | ) { 314 | const { botToken } = await db.loadWorkspace(workspace) 315 | const { ok } = await Slack.dialog.open({ 316 | token: botToken, 317 | trigger_id: payload.trigger_id, 318 | dialog: { 319 | callback_id: uuid(), 320 | title, 321 | submit_label: 'Submit', 322 | state: JSON.stringify({ 323 | action, 324 | state, 325 | }), 326 | elements, 327 | }, 328 | }) 329 | return ok 330 | } 331 | 332 | function openLinkForOtherDialog( 333 | workspace: string, 334 | payload: any, 335 | githubName: string, 336 | state: { githubName: string }, 337 | ) { 338 | const elements = [ 339 | { 340 | label: `Who is ${githubName}?`, 341 | name: 'slackUser', 342 | type: 'select', 343 | hint: `The user will receive notifications about related PR in the future.`, 344 | data_source: 'users', 345 | }, 346 | ] 347 | return openSlackDialog( 348 | workspace, 349 | payload, 350 | actions.linkOtherUser, 351 | state, 352 | `Link for ${githubName}`, 353 | elements, 354 | ) 355 | } 356 | 357 | function openLinkDialog( 358 | workspace: string, 359 | payload: SlackPayload, 360 | githubNames?: string[], 361 | state?: { 362 | githubName?: string 363 | }, 364 | ) { 365 | const elements = [ 366 | { 367 | label: `GitHub Username`, 368 | name: 'github_name', 369 | type: 'text', 370 | hint: 371 | githubNames && githubNames.length 372 | ? `You have been linked to ${githubNames.join(', ')}. You can add more.` 373 | : `Input your GitHub username here, you'll be notified on Slack when requested to review Pull Requests.`, 374 | placeholder: 'your-github-username', 375 | max_length: 24, 376 | }, 377 | ] 378 | return openSlackDialog(workspace, payload, actions.link, state, `Link to GitHub`, elements) 379 | } 380 | 381 | function openUnlinkDialog( 382 | workspace: string, 383 | payload: SlackPayload, 384 | githubNames: string[], 385 | state?: { 386 | githubName?: string 387 | }, 388 | ) { 389 | const elements = [ 390 | { 391 | label: `GitHub Username`, 392 | name: 'github_name', 393 | type: 'select', 394 | hint: 'Which GitHub user would you like to unlink?', 395 | options: githubNames.map(githubName => ({ 396 | label: githubName, 397 | value: githubName, 398 | })), 399 | }, 400 | ] 401 | return openSlackDialog(workspace, payload, actions.unlink, state, `Undo link`, elements) 402 | } 403 | 404 | export const handleOAuth: RouteHandler = async function handleOAuth(req, data) { 405 | const url = getURL(req) 406 | const code = url.searchParams.get('code') 407 | 408 | try { 409 | const { 410 | access_token: accessToken, 411 | team_id: workspace, 412 | bot: { bot_user_id: botID, bot_access_token: botToken }, 413 | } = await Slack.oauth.access({ 414 | client_secret: clientSecret, 415 | client_id: clientID, 416 | code, 417 | }) 418 | await db.createWorkspace(workspace, { accessToken, botID, botToken }) 419 | return `Well done! GitHub Code Review Notifier have been added to your workspace. Check out @CodeReviewNotifier on Slack!` 420 | } catch (error) { 421 | console.error(error) 422 | return `Something went wrong :(` 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | const { 2 | FIREBASE, 3 | VERIFICATION_TOKEN, 4 | CLIENT_SECRET, 5 | SIGNING_SECRET, 6 | CLIENT_ID, 7 | LOG_REQUEST_ON_ERROR, 8 | SENTRY_PUBLIC_KEY, 9 | SENTRY_PROJECT_ID, 10 | } = process.env 11 | 12 | if (!FIREBASE) throw Error('missing Firebase') 13 | if (!VERIFICATION_TOKEN) throw Error('missing Slack App verification token') 14 | if (!CLIENT_SECRET) throw Error('missing client secret') 15 | if (!SIGNING_SECRET) throw Error('missing signing secret') 16 | if (!CLIENT_ID) throw Error('missing client ID') 17 | if (!SENTRY_PUBLIC_KEY) throw Error('missing sentry public key') 18 | if (!SENTRY_PROJECT_ID) throw Error('missing sentry project ID') 19 | 20 | export const firebaseConfig = JSON.parse(Buffer.from(FIREBASE, 'base64').toString()) 21 | export const verificationToken = VERIFICATION_TOKEN 22 | export const clientSecret = CLIENT_SECRET 23 | export const signingSecret = SIGNING_SECRET 24 | export const clientID = CLIENT_ID 25 | export const logRequestOnError = LOG_REQUEST_ON_ERROR === 'true' 26 | export const decodePayload = true 27 | 28 | export const sentryPublicKey = SENTRY_PUBLIC_KEY 29 | export const sentryProjectId = SENTRY_PROJECT_ID 30 | export const sentryDSN = `https://${sentryPublicKey}@sentry.io/${sentryProjectId}` 31 | 32 | export const IN_PRODUCTION_MODE = process.env.NODE_ENV === 'production' 33 | -------------------------------------------------------------------------------- /src/db.ts: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase' 2 | import { firebaseConfig } from './config' 3 | import { dePromiseLike } from './utils' 4 | 5 | // initialize and basic operations 6 | 7 | const app = firebase.initializeApp(firebaseConfig) 8 | const database = app.database() 9 | 10 | type RefPath = string 11 | 12 | function getRef(path: RefPath) { 13 | return database.ref(path) 14 | } 15 | 16 | function add(path: RefPath, value: T) { 17 | return dePromiseLike(getRef(path).push(value)) 18 | } 19 | 20 | function set(path: RefPath, value: T) { 21 | return getRef(path).set(value) 22 | } 23 | 24 | function remove(path: RefPath) { 25 | return getRef(path).remove() 26 | } 27 | 28 | async function get(path: RefPath) { 29 | const snapshot = await getRef(path).once('value') 30 | return snapshot.val() as T | null 31 | } 32 | 33 | function find(path: RefPath, key: string, value: string | number | boolean | null) { 34 | return getRef(path) 35 | .orderByChild(key) 36 | .equalTo(value) 37 | } 38 | 39 | // database model helpers 40 | 41 | const keys = { 42 | registered: `registered`, 43 | link: `link`, 44 | slackUserID: `slack`, 45 | githubName: `github`, 46 | log: `logs`, 47 | } 48 | 49 | const paths: { 50 | [key: string]: (seed: string) => string 51 | } = { 52 | registered: workspace => `${keys.registered}/${workspace}`, 53 | link: workspace => `${keys.link}/${workspace}`, 54 | } 55 | 56 | // business logics 57 | 58 | export function saveLink(workspace: string, link: GSLink) { 59 | return add(paths.link(workspace), link) 60 | } 61 | 62 | function getGitHubLinkQuery(workspace: string, github: GSLink['github']) { 63 | return find(paths.link(workspace), keys.githubName, github) 64 | } 65 | 66 | function getSlackLinkQuery(workspace: string, slack: GSLink['slack']) { 67 | return find(paths.link(workspace), keys.slackUserID, slack) 68 | } 69 | 70 | function getLinkQuery(workspace: string, link: Pick | Pick) { 71 | let query: firebase.database.Query 72 | if ('github' in link) query = getGitHubLinkQuery(workspace, link.github) 73 | else if ('slack' in link) query = getSlackLinkQuery(workspace, link.slack) 74 | else throw new Error('Unexpected link: ' + JSON.stringify(link)) 75 | return query 76 | } 77 | 78 | export async function removeLink( 79 | workspace: string, 80 | link: Pick | Pick, 81 | ) { 82 | const query: firebase.database.Query = getLinkQuery(workspace, link) 83 | 84 | const snapshot = await query.limitToFirst(1).once('value') 85 | if (snapshot.exists()) { 86 | const promisesOfRemove: Promise[] = [] 87 | snapshot.forEach(child => { 88 | promisesOfRemove.push(child.ref.remove()) 89 | }) 90 | return Promise.all(promisesOfRemove) 91 | } 92 | } 93 | 94 | export async function loadLinks( 95 | workspace: string, 96 | link: Pick | Pick, 97 | ) { 98 | const query: firebase.database.Query = getLinkQuery(workspace, link) 99 | 100 | const snapshot = await query.once('value') 101 | if (snapshot.exists()) { 102 | return Object.values( 103 | snapshot.val() as { 104 | [key: string]: GSLink 105 | }, 106 | ) 107 | } 108 | } 109 | 110 | export async function loadWorkspace(workspace: string) { 111 | const value = await get(paths.registered(workspace)) 112 | if (value === null) throw new Error(`Cannot find workspace "${workspace}"`) 113 | return value 114 | } 115 | 116 | export function createWorkspace(workspace: string, workspaceMeta: WorkspaceMeta) { 117 | return set(paths.registered(workspace), workspaceMeta) 118 | } 119 | 120 | export function log(content: { [key: string]: string | undefined }) { 121 | return getRef(keys.log).push(content) 122 | } 123 | -------------------------------------------------------------------------------- /src/format.ts: -------------------------------------------------------------------------------- 1 | export const mention = (userID: string | null) => `<@${userID}>` 2 | 3 | export const slackLink = (url: string, label: string) => `<${url}|${label}>` 4 | 5 | export const githubUserPage = (githubName: string) => `https://github.com/${githubName}` 6 | 7 | export const githubUserPageLink = (githubName: string) => 8 | slackLink(githubUserPage(githubName), githubName) 9 | 10 | export function pullRequestLabel( 11 | number: number, 12 | pullRequestTitle: string, 13 | repoName: string, 14 | ): string { 15 | return `*#${number} ${pullRequestTitle.replace(/\+/g, ' ')}* in ${repoName}` 16 | } 17 | -------------------------------------------------------------------------------- /src/github.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from '../extra' 2 | import { getURL, RouteHandler } from './' 3 | import { actions, botSpeak } from './bot' 4 | import * as db from './db' 5 | import { 6 | githubUserPageLink, 7 | mention, 8 | pullRequestLabel, 9 | slackLink, 10 | } from './format' 11 | 12 | const GITHUB_EVENT_HEADER_KEY = 'X-GitHub-Event' 13 | 14 | const GITHUB_EVENT_TYPES = { 15 | PING: 'ping', 16 | PULL_REQUEST: 'pull_request', 17 | PULL_REQUEST_REVIEW: 'pull_request_review', 18 | } 19 | 20 | const GITHUB_EVENT_ACTION_TYPES = { 21 | REVIEW_REQUESTED: 'review_requested', 22 | SUBMITTED: 'submitted', 23 | } 24 | 25 | const getHeader = (req: IncomingMessage, key: string) => 26 | req.headers && (req.headers[key] || req.headers[key.toLowerCase()]) 27 | 28 | const getWorkspace = (req: IncomingMessage) => { 29 | const url = getURL(req) 30 | const workspace = url.searchParams.get('workspace') 31 | if (!workspace) throw Error(`no workspace provided`) 32 | return workspace 33 | } 34 | 35 | const menuForLinkingOthers = (githubName: string) => ({ 36 | attachments: [ 37 | { 38 | text: `If ${githubName} is in this workspace, you can...`, 39 | fallback: 'Something went wrong.', 40 | callback_id: 'link_for_others', 41 | color: '#3AA3E3', 42 | attachment_type: 'default', 43 | actions: [ 44 | { 45 | text: `Link for ${githubName}`, 46 | type: 'button', 47 | name: actions.linkOtherUser, 48 | value: JSON.stringify({ githubName }), 49 | }, 50 | ], 51 | }, 52 | ], 53 | }) 54 | 55 | export const handleGitHubHook: RouteHandler = async (req, data) => { 56 | // handle application/x-www-form-urlencoded data 57 | // which sends stringified JSON data in `payload` field 58 | if (data.payload) data = JSON.parse(data.payload) 59 | 60 | const workspace = getWorkspace(req) 61 | const type = getHeader(req, GITHUB_EVENT_HEADER_KEY) 62 | if (!type) throw Error(`no github event header provided`) 63 | switch (type) { 64 | case GITHUB_EVENT_TYPES.PING: 65 | return `I'm ready!` 66 | case GITHUB_EVENT_TYPES.PULL_REQUEST: 67 | switch (data['action']) { 68 | case GITHUB_EVENT_ACTION_TYPES.REVIEW_REQUESTED: { 69 | return handleReviewRequested(workspace, data) 70 | } 71 | default: 72 | return 'unresolved action' 73 | } 74 | case GITHUB_EVENT_TYPES.PULL_REQUEST_REVIEW: 75 | switch (data.action) { 76 | case GITHUB_EVENT_ACTION_TYPES.SUBMITTED: 77 | return handleSubmittedPullRequestReview(workspace, data) 78 | default: 79 | return 'unresolved action' 80 | } 81 | default: 82 | return `no handler for this event type` 83 | } 84 | } 85 | 86 | async function handleReviewRequested(workspace: string, data: any) { 87 | // Sometimes there is no requested reviewer data provided 88 | if (!data['requested_reviewer']) { 89 | console.log(`No reviewer requested`) 90 | return 91 | } 92 | const { login: reviewerGitHubName } = data['requested_reviewer'] 93 | const { 94 | user: { login: requesterGitHubName }, 95 | html_url: pullRequestURL, 96 | title: pullRequestTitle, 97 | number, 98 | } = data['pull_request'] 99 | 100 | const repoName = 101 | ( 102 | data['repository'] || 103 | { 104 | /* In case no repository data were available */ 105 | } 106 | ).full_name || extractRepoNameFromURL(pullRequestURL) 107 | 108 | const [requesterUserID, reviewerUserID] = await Promise.all([ 109 | githubNameToSlackID(workspace, requesterGitHubName), 110 | githubNameToSlackID(workspace, reviewerGitHubName), 111 | ]) 112 | 113 | type Pair = { 114 | slackUserID: string 115 | githubName: string 116 | } 117 | const pairs = [ 118 | { 119 | slackUserID: reviewerUserID, 120 | githubName: reviewerGitHubName, 121 | }, 122 | { 123 | slackUserID: requesterUserID, 124 | githubName: requesterGitHubName, 125 | }, 126 | ] 127 | const linkedUsers: Pair[] = [] 128 | const notLinkedGitHubNames: string[] = [] 129 | pairs.forEach(pair => { 130 | if (pair.slackUserID) { 131 | linkedUsers.push(pair as Pair) // it's safe 132 | } else { 133 | notLinkedGitHubNames.push(pair.githubName) 134 | } 135 | }) 136 | 137 | if (linkedUsers.length === 0) { 138 | console.log( 139 | `could not find users for`, 140 | requesterGitHubName, 141 | `nor`, 142 | reviewerGitHubName, 143 | ) 144 | return 145 | } 146 | 147 | const formattedPRLink = slackLink( 148 | pullRequestURL, 149 | pullRequestLabel(number, pullRequestTitle, repoName), 150 | ) 151 | const mainContent = `🧐 ${requesterGitHubName}(${mention( 152 | requesterUserID, 153 | )}) requested code review from ${reviewerGitHubName}(${mention( 154 | reviewerUserID, 155 | )}):\n${formattedPRLink}` 156 | 157 | const text = notLinkedGitHubNames.length 158 | ? `${mainContent}\n\nNote: ${notLinkedGitHubNames 159 | .map(githubUserPageLink) 160 | .join(', ')} has not been linked to this workspace yet.` 161 | : mainContent 162 | 163 | return Promise.all( 164 | linkedUsers.map(({ slackUserID }) => { 165 | if (notLinkedGitHubNames.length === 0) { 166 | return botSpeak(workspace, slackUserID, text) 167 | } else if (notLinkedGitHubNames.length === 1) { 168 | const [githubName] = notLinkedGitHubNames 169 | return botSpeak( 170 | workspace, 171 | slackUserID, 172 | text, 173 | menuForLinkingOthers(githubName), 174 | ) 175 | } 176 | throw new Error('Cannot handle multiple not linked users yet.') 177 | }), 178 | ) 179 | } 180 | 181 | async function handleSubmittedPullRequestReview(workspace: string, data: any) { 182 | const { 183 | pull_request: { 184 | user: { login: requesterGitHubName }, 185 | html_url: pullRequestURL, 186 | title: pullRequestTitle, 187 | number, 188 | }, 189 | review: { 190 | state, 191 | html_url: reviewUrl, 192 | user: { login: reviewerGitHubName }, 193 | }, 194 | } = data 195 | 196 | if (reviewerGitHubName === requesterGitHubName) { 197 | // self comment, ignore 198 | return 199 | } 200 | 201 | const repoName = 202 | ( 203 | data['repository'] || 204 | { 205 | /* In case no repository data were available */ 206 | } 207 | ).full_name || extractRepoNameFromURL(pullRequestURL) 208 | 209 | const formattedPRLink = slackLink( 210 | reviewUrl, 211 | pullRequestLabel(number, pullRequestTitle, repoName), 212 | ) 213 | 214 | const [requesterUserID, reviewerUserID] = await Promise.all([ 215 | githubNameToSlackID(workspace, requesterGitHubName), 216 | githubNameToSlackID(workspace, reviewerGitHubName), 217 | ]) 218 | if (!requesterUserID && !reviewerUserID) { 219 | console.log( 220 | `Could not find user for neither ${requesterGitHubName} nor ${reviewerGitHubName}`, 221 | ) 222 | return 223 | } 224 | if (state === 'approved') { 225 | // approvement message, notify requestor 226 | if (requesterUserID) { 227 | return botSpeak( 228 | workspace, 229 | requesterUserID, 230 | `🎉 Your pull request has been approved!\n${formattedPRLink}`, 231 | ) 232 | } else if (reviewerUserID) { 233 | // we could ask reviewer to introduce this app to PR requester here, but not now 234 | } else { 235 | throw new Error('impossible') 236 | } 237 | } else if (state === 'changes_requested') { 238 | // review message 239 | if (requesterUserID) { 240 | let text = `📝 ${reviewerGitHubName}(${mention( 241 | reviewerUserID, 242 | )}) has requested changes in ${requesterGitHubName}(${mention( 243 | requesterUserID, 244 | )})'s pull request\n${formattedPRLink}` 245 | if (!reviewerUserID) { 246 | const linkNotify = (githubName: string) => 247 | `\n\nNote: ${githubName} has not been linked to this workspace yet.` 248 | text += linkNotify(reviewerGitHubName) 249 | } 250 | return botSpeak(workspace, requesterUserID, text) 251 | } else if (reviewerUserID) { 252 | // we could ask reviewer to introduce this app to PR requester here, but not now 253 | } else { 254 | throw new Error('impossible') 255 | } 256 | } else { 257 | // review message 258 | if (requesterUserID) { 259 | let text = `👏 ${requesterGitHubName}(${mention( 260 | requesterUserID, 261 | )})'s pull request has been reviewed by ${reviewerGitHubName}(${mention( 262 | reviewerUserID, 263 | )})\n${formattedPRLink}` 264 | if (!reviewerUserID) { 265 | const linkNotify = (githubName: string) => 266 | `\n\nNote: ${githubName} has not been linked to this workspace yet.` 267 | text += linkNotify(reviewerGitHubName) 268 | } 269 | return botSpeak(workspace, requesterUserID, text) 270 | } else if (reviewerUserID) { 271 | // we could ask reviewer to introduce this app to PR requester here, but not now 272 | } else { 273 | throw new Error('impossible') 274 | } 275 | } 276 | } 277 | 278 | function extractRepoNameFromURL(pullRequestURL: string) { 279 | const pullRequestURLRegexp = /^https:\/\/github.com\/(.*?\/.*?)\/pull\/(\d+)$/ 280 | const matched = (pullRequestURL).match(pullRequestURLRegexp) 281 | if (matched) { 282 | return matched[1] 283 | } 284 | } 285 | 286 | function githubNameToSlackID( 287 | workspace: string, 288 | githubName: string, 289 | ): Promise { 290 | return db 291 | .loadLinks(workspace, { github: githubName }) 292 | .then(links => (links ? links[0].slack : null)) 293 | } 294 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from '@sentry/node' 2 | import { URL } from 'url' 3 | import { IncomingMessage, RequestListener } from '../extra' 4 | import { decodePayload, IN_PRODUCTION_MODE, logRequestOnError, sentryDSN } from './config' 5 | import { log } from './db' 6 | 7 | Sentry.init({ 8 | dsn: sentryDSN, 9 | environment: IN_PRODUCTION_MODE ? 'production' : 'development', 10 | }) 11 | 12 | export type Route = { 13 | path: string 14 | handler?: RequestListener 15 | } 16 | 17 | export type RouteHandler = ( 18 | req: IncomingMessage, 19 | data: ExpectedAny, 20 | ) => Promise | T 21 | 22 | export const getURL = (req: IncomingMessage) => new URL(`https://${req.headers.host}${req.url}`) 23 | 24 | const wwwFormParser = (body: string) => 25 | body 26 | .split('&') 27 | .map(pair => pair.split('=')) 28 | .map(pair => pair.map(decodeURIComponent)) 29 | .reduce( 30 | (merged, [key, value]) => { 31 | if (key in merged) { 32 | if (Array.isArray(merged[key])) (merged[key] as string[]).push(value) 33 | else merged[key] = [merged[key] as string, value] 34 | } else merged[key] = value 35 | 36 | return merged 37 | }, 38 | {} as { 39 | [key: string]: string | string[] 40 | }, 41 | ) 42 | 43 | const getContentParser = (req: IncomingMessage) => { 44 | switch (req.headers['content-type']) { 45 | case 'application/x-www-form-urlencoded': 46 | return wwwFormParser 47 | case 'application/json': 48 | return JSON.parse 49 | default: 50 | return (_: T) => _ 51 | } 52 | } 53 | 54 | const getRequestBody = (req: IncomingMessage) => { 55 | return new Promise((resolve, reject) => { 56 | const bodyBuffer: string[] = [] 57 | req.on('data', data => bodyBuffer.push(data.toString())) 58 | req.on('end', async () => { 59 | const body = bodyBuffer.join('') 60 | resolve(body) 61 | }) 62 | req.on('error', reject) 63 | }) 64 | } 65 | 66 | async function parseContent(req: IncomingMessage) { 67 | const body = await getRequestBody(req) 68 | const parser = getContentParser(req) 69 | const data = parser(body) 70 | return data 71 | } 72 | 73 | export const requestHandler: (handler: RouteHandler) => RequestListener = handler => async ( 74 | req, 75 | res, 76 | ) => { 77 | let data: ExpectedAny 78 | let result 79 | try { 80 | data = await parseContent(req) 81 | result = await handler(req, data) 82 | res.end(result ? JSON.stringify(result) : undefined) 83 | } catch (err) { 84 | console.error(err) 85 | if (decodePayload) { 86 | if (typeof data === 'object' && data !== null && typeof data.payload === 'string') { 87 | try { 88 | data.payload = JSON.parse(decodeURIComponent(data.payload)) 89 | } catch (err) {} 90 | } 91 | } 92 | if (logRequestOnError) { 93 | const logRef = await log({ 94 | time: new Date().toLocaleString('US'), 95 | path: req.url, 96 | info: String(err), 97 | data, 98 | }) 99 | Sentry.withScope(scope => { 100 | scope.setExtra('path', req.url) 101 | scope.setExtra('data', data) 102 | scope.setExtra('firebase-log-key', logRef.key) 103 | Sentry.captureException(err) 104 | }) 105 | } else { 106 | console.log('not logging above error to db') 107 | } 108 | res.writeHead(400) 109 | res.end(String(err)) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/proxy.ts: -------------------------------------------------------------------------------- 1 | import httpProxy from 'http-proxy' 2 | 3 | export const proxy = httpProxy.createProxyServer({ 4 | secure: true, 5 | autoRewrite: true, 6 | changeOrigin: true, 7 | }) 8 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function dePromiseLike(pLike: PromiseLike): Promise { 2 | return new Promise(resolve => pLike.then(resolve)) 3 | } 4 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, 6 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | "sourceMap": true /* Generates corresponding '.map' file. */, 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./build" /* Redirect output structure to the directory. */, 16 | "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true /* Enable all strict type-checking options. */, 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@firebase/analytics-types@0.4.0": 6 | version "0.4.0" 7 | resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.4.0.tgz#d6716f9fa36a6e340bc0ecfe68af325aa6f60508" 8 | integrity sha512-Jj2xW+8+8XPfWGkv9HPv/uR+Qrmq37NPYT352wf7MvE9LrstpLVmFg3LqG6MCRr5miLAom5sen2gZ+iOhVDeRA== 9 | 10 | "@firebase/analytics@0.6.9": 11 | version "0.6.9" 12 | resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.6.9.tgz#1f6d015e27beb6e0be2363908803b3c57d98f1da" 13 | integrity sha512-G0PkfMq/4tpDXwk/S2LKrXUWiz5tpQ6o2Lf6esgdEcDLpimPl32TrioNkDEDz8Xp0mzpY04UKwvYjT5xuzoKug== 14 | dependencies: 15 | "@firebase/analytics-types" "0.4.0" 16 | "@firebase/component" "0.4.1" 17 | "@firebase/installations" "0.4.25" 18 | "@firebase/logger" "0.2.6" 19 | "@firebase/util" "1.0.0" 20 | tslib "^2.1.0" 21 | 22 | "@firebase/app-types@0.6.2": 23 | version "0.6.2" 24 | resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.2.tgz#8578cb1061a83ced4570188be9e225d54e0f27fb" 25 | integrity sha512-2VXvq/K+n8XMdM4L2xy5bYp2ZXMawJXluUIDzUBvMthVR+lhxK4pfFiqr1mmDbv9ydXvEAuFsD+6DpcZuJcSSw== 26 | 27 | "@firebase/app@0.6.20": 28 | version "0.6.20" 29 | resolved "https://registry.yarnpkg.com/@firebase/app/-/app-0.6.20.tgz#07d3bdda6fbe34bac34bcba7f2f92ce394a29753" 30 | integrity sha512-5zstJ3Cxw9H5cxfdaAhCH7WHVaRLPhCcgVNwKp6dWeTx2QkIdNvHainX8Vr2RaZchw4MxRjkPfwNVOaq2oFStQ== 31 | dependencies: 32 | "@firebase/app-types" "0.6.2" 33 | "@firebase/component" "0.4.1" 34 | "@firebase/logger" "0.2.6" 35 | "@firebase/util" "1.0.0" 36 | dom-storage "2.1.0" 37 | tslib "^2.1.0" 38 | xmlhttprequest "1.8.0" 39 | 40 | "@firebase/auth-interop-types@0.1.6": 41 | version "0.1.6" 42 | resolved "https://registry.yarnpkg.com/@firebase/auth-interop-types/-/auth-interop-types-0.1.6.tgz#5ce13fc1c527ad36f1bb1322c4492680a6cf4964" 43 | integrity sha512-etIi92fW3CctsmR9e3sYM3Uqnoq861M0Id9mdOPF6PWIg38BXL5k4upCNBggGUpLIS0H1grMOvy/wn1xymwe2g== 44 | 45 | "@firebase/auth-types@0.10.3": 46 | version "0.10.3" 47 | resolved "https://registry.yarnpkg.com/@firebase/auth-types/-/auth-types-0.10.3.tgz#2be7dd93959c8f5304c63e09e98718e103464d8c" 48 | integrity sha512-zExrThRqyqGUbXOFrH/sowuh2rRtfKHp9SBVY2vOqKWdCX1Ztn682n9WLtlUDsiYVIbBcwautYWk2HyCGFv0OA== 49 | 50 | "@firebase/auth@0.16.5": 51 | version "0.16.5" 52 | resolved "https://registry.yarnpkg.com/@firebase/auth/-/auth-0.16.5.tgz#703a9f1208e14fa0801798bd926c4a8f59fc97c4" 53 | integrity sha512-Cgs/TlVot2QkbJyEphvKmu+2qxYlNN+Q2+29aqZwryrnn1eLwlC7nT89K6O91/744HJRtiThm02bMj2Wh61E3Q== 54 | dependencies: 55 | "@firebase/auth-types" "0.10.3" 56 | 57 | "@firebase/component@0.4.1": 58 | version "0.4.1" 59 | resolved "https://registry.yarnpkg.com/@firebase/component/-/component-0.4.1.tgz#c8269f21149a4c81e385531428ad4c086a8f47db" 60 | integrity sha512-f0IbIsoe33QzOj554rmDL04PyeZX/nNZYOAwlTzKmHq/JoFN6YoySi+0ZLyCtFrnRgw6zNnR/POXKOdfljWqZA== 61 | dependencies: 62 | "@firebase/util" "1.0.0" 63 | tslib "^2.1.0" 64 | 65 | "@firebase/database-types@0.7.2": 66 | version "0.7.2" 67 | resolved "https://registry.yarnpkg.com/@firebase/database-types/-/database-types-0.7.2.tgz#449c4b36ec59a1ad9089797b540e2ba1c0d4fcbf" 68 | integrity sha512-cdAd/dgwvC0r3oLEDUR+ULs1vBsEvy0b27nlzKhU6LQgm9fCDzgaH9nFGv8x+S9dly4B0egAXkONkVoWcOAisg== 69 | dependencies: 70 | "@firebase/app-types" "0.6.2" 71 | 72 | "@firebase/database@0.9.11": 73 | version "0.9.11" 74 | resolved "https://registry.yarnpkg.com/@firebase/database/-/database-0.9.11.tgz#87fba58cb6f3e93d56a88b9130507c9988fa2f97" 75 | integrity sha512-f1+39Q1Zxnnu7sswz5oOZUC3CTMEEQR6hFxmE+ZJiw5+/Uy/wK11DS/KofSj0VCe0cPhPHxNzmLjUU5/5yu9Kw== 76 | dependencies: 77 | "@firebase/auth-interop-types" "0.1.6" 78 | "@firebase/component" "0.4.1" 79 | "@firebase/database-types" "0.7.2" 80 | "@firebase/logger" "0.2.6" 81 | "@firebase/util" "1.0.0" 82 | faye-websocket "0.11.3" 83 | tslib "^2.1.0" 84 | 85 | "@firebase/firestore-types@2.2.0": 86 | version "2.2.0" 87 | resolved "https://registry.yarnpkg.com/@firebase/firestore-types/-/firestore-types-2.2.0.tgz#9a3f3f2906232c3b4a726d988a6ef077f35f9093" 88 | integrity sha512-5kZZtQ32FIRJP1029dw+ZVNRCclKOErHv1+Xn0pw/5Fq3dxroA/ZyFHqDu+uV52AyWHhNLjCqX43ibm4YqOzRw== 89 | 90 | "@firebase/firestore@2.2.5": 91 | version "2.2.5" 92 | resolved "https://registry.yarnpkg.com/@firebase/firestore/-/firestore-2.2.5.tgz#a7fdc3edcf4c165f827bec3b80115099462d6482" 93 | integrity sha512-Ucg3cy79u4KPlPs5//c5Af92OrZJigSUem2JxWKHlGSgjl71CR6Pa9WMkv6ot5qNJcxwx4FdDtqrtIpKA/xPDw== 94 | dependencies: 95 | "@firebase/component" "0.4.1" 96 | "@firebase/firestore-types" "2.2.0" 97 | "@firebase/logger" "0.2.6" 98 | "@firebase/util" "1.0.0" 99 | "@firebase/webchannel-wrapper" "0.4.1" 100 | "@grpc/grpc-js" "^1.0.0" 101 | "@grpc/proto-loader" "^0.5.0" 102 | node-fetch "2.6.1" 103 | tslib "^2.1.0" 104 | 105 | "@firebase/functions-types@0.4.0": 106 | version "0.4.0" 107 | resolved "https://registry.yarnpkg.com/@firebase/functions-types/-/functions-types-0.4.0.tgz#0b789f4fe9a9c0b987606c4da10139345b40f6b9" 108 | integrity sha512-3KElyO3887HNxtxNF1ytGFrNmqD+hheqjwmT3sI09FaDCuaxGbOnsXAXH2eQ049XRXw9YQpHMgYws/aUNgXVyQ== 109 | 110 | "@firebase/functions@0.6.7": 111 | version "0.6.7" 112 | resolved "https://registry.yarnpkg.com/@firebase/functions/-/functions-0.6.7.tgz#3a5be76edeb990b8bb80c0e68755baa2f46362c8" 113 | integrity sha512-IDw2ww28Tj8t947ySVO9wHghlwNl4bIUo5tPUzAbipfgLlj3GeHwqhvSv++O/ILBu4Rk7KD7cbxtw/rziATHNA== 114 | dependencies: 115 | "@firebase/component" "0.4.1" 116 | "@firebase/functions-types" "0.4.0" 117 | "@firebase/messaging-types" "0.5.0" 118 | node-fetch "2.6.1" 119 | tslib "^2.1.0" 120 | 121 | "@firebase/installations-types@0.3.4": 122 | version "0.3.4" 123 | resolved "https://registry.yarnpkg.com/@firebase/installations-types/-/installations-types-0.3.4.tgz#589a941d713f4f64bf9f4feb7f463505bab1afa2" 124 | integrity sha512-RfePJFovmdIXb6rYwtngyxuEcWnOrzdZd9m7xAW0gRxDIjBT20n3BOhjpmgRWXo/DAxRmS7bRjWAyTHY9cqN7Q== 125 | 126 | "@firebase/installations@0.4.25": 127 | version "0.4.25" 128 | resolved "https://registry.yarnpkg.com/@firebase/installations/-/installations-0.4.25.tgz#532a50418afc01b3cbc8fdab55e3b168cec66193" 129 | integrity sha512-szQ2bpI5NHTRuZAqXNZLq7bkZ1iTURPmojj7xWjBRxyMnDd6lLQ/Ht8Wut0ESH7uzbFNqmZ9oBMh2U9fpBIniA== 130 | dependencies: 131 | "@firebase/component" "0.4.1" 132 | "@firebase/installations-types" "0.3.4" 133 | "@firebase/util" "1.0.0" 134 | idb "3.0.2" 135 | tslib "^2.1.0" 136 | 137 | "@firebase/logger@0.2.6": 138 | version "0.2.6" 139 | resolved "https://registry.yarnpkg.com/@firebase/logger/-/logger-0.2.6.tgz#3aa2ca4fe10327cabf7808bd3994e88db26d7989" 140 | integrity sha512-KIxcUvW/cRGWlzK9Vd2KB864HlUnCfdTH0taHE0sXW5Xl7+W68suaeau1oKNEqmc3l45azkd4NzXTCWZRZdXrw== 141 | 142 | "@firebase/messaging-types@0.5.0": 143 | version "0.5.0" 144 | resolved "https://registry.yarnpkg.com/@firebase/messaging-types/-/messaging-types-0.5.0.tgz#c5d0ef309ced1758fda93ef3ac70a786de2e73c4" 145 | integrity sha512-QaaBswrU6umJYb/ZYvjR5JDSslCGOH6D9P136PhabFAHLTR4TWjsaACvbBXuvwrfCXu10DtcjMxqfhdNIB1Xfg== 146 | 147 | "@firebase/messaging@0.7.9": 148 | version "0.7.9" 149 | resolved "https://registry.yarnpkg.com/@firebase/messaging/-/messaging-0.7.9.tgz#b97acca8900eba6a04d8510638a6986172436675" 150 | integrity sha512-zzEmtpBdauT0n0JA5eN/dHeQZkQj/bbfl7CNmhA0EpKU2wTRFZCJYAOZkZEw8OD9/D/aDRcEk3Qq+5I1XcugZA== 151 | dependencies: 152 | "@firebase/component" "0.4.1" 153 | "@firebase/installations" "0.4.25" 154 | "@firebase/messaging-types" "0.5.0" 155 | "@firebase/util" "1.0.0" 156 | idb "3.0.2" 157 | tslib "^2.1.0" 158 | 159 | "@firebase/performance-types@0.0.13": 160 | version "0.0.13" 161 | resolved "https://registry.yarnpkg.com/@firebase/performance-types/-/performance-types-0.0.13.tgz#58ce5453f57e34b18186f74ef11550dfc558ede6" 162 | integrity sha512-6fZfIGjQpwo9S5OzMpPyqgYAUZcFzZxHFqOyNtorDIgNXq33nlldTL/vtaUZA8iT9TT5cJlCrF/jthKU7X21EA== 163 | 164 | "@firebase/performance@0.4.11": 165 | version "0.4.11" 166 | resolved "https://registry.yarnpkg.com/@firebase/performance/-/performance-0.4.11.tgz#928132583219a15bb64049e76f8d7c5a5045e7d6" 167 | integrity sha512-SQb9QpAkgpPS1QnRLxNAXFTCrW/VT9MidVcJVHuBrCCW9sYY+QVuuWYpaGR4zQDsTx2e/UGUXJgw+z0vaQ0Q6w== 168 | dependencies: 169 | "@firebase/component" "0.4.1" 170 | "@firebase/installations" "0.4.25" 171 | "@firebase/logger" "0.2.6" 172 | "@firebase/performance-types" "0.0.13" 173 | "@firebase/util" "1.0.0" 174 | tslib "^2.1.0" 175 | 176 | "@firebase/polyfill@0.3.36": 177 | version "0.3.36" 178 | resolved "https://registry.yarnpkg.com/@firebase/polyfill/-/polyfill-0.3.36.tgz#c057cce6748170f36966b555749472b25efdb145" 179 | integrity sha512-zMM9oSJgY6cT2jx3Ce9LYqb0eIpDE52meIzd/oe/y70F+v9u1LDqk5kUF5mf16zovGBWMNFmgzlsh6Wj0OsFtg== 180 | dependencies: 181 | core-js "3.6.5" 182 | promise-polyfill "8.1.3" 183 | whatwg-fetch "2.0.4" 184 | 185 | "@firebase/remote-config-types@0.1.9": 186 | version "0.1.9" 187 | resolved "https://registry.yarnpkg.com/@firebase/remote-config-types/-/remote-config-types-0.1.9.tgz#fe6bbe4d08f3b6e92fce30e4b7a9f4d6a96d6965" 188 | integrity sha512-G96qnF3RYGbZsTRut7NBX0sxyczxt1uyCgXQuH/eAfUCngxjEGcZQnBdy6mvSdqdJh5mC31rWPO4v9/s7HwtzA== 189 | 190 | "@firebase/remote-config@0.1.36": 191 | version "0.1.36" 192 | resolved "https://registry.yarnpkg.com/@firebase/remote-config/-/remote-config-0.1.36.tgz#02a2f5799d22728ac30a28273cbd299c82fda33a" 193 | integrity sha512-aQXaBDkEzFix3ycjPiP+4OPSXZmUbFunOiVi20XS9kRZrZfNhCH3HdBYwL1Nl9/AvcOnlZfX+lqa2LuHVXmuwA== 194 | dependencies: 195 | "@firebase/component" "0.4.1" 196 | "@firebase/installations" "0.4.25" 197 | "@firebase/logger" "0.2.6" 198 | "@firebase/remote-config-types" "0.1.9" 199 | "@firebase/util" "1.0.0" 200 | tslib "^2.1.0" 201 | 202 | "@firebase/storage-types@0.4.1": 203 | version "0.4.1" 204 | resolved "https://registry.yarnpkg.com/@firebase/storage-types/-/storage-types-0.4.1.tgz#da6582ae217e3db485c90075dc71100ca5064cc6" 205 | integrity sha512-IM4cRzAnQ6QZoaxVZ5MatBzqXVcp47hOlE28jd9xXw1M9V7gfjhmW0PALGFQx58tPVmuUwIKyoEbHZjV4qRJwQ== 206 | 207 | "@firebase/storage@0.5.1": 208 | version "0.5.1" 209 | resolved "https://registry.yarnpkg.com/@firebase/storage/-/storage-0.5.1.tgz#a05be43e8ad9be2304bf5cd6d1a13e938db9f157" 210 | integrity sha512-cDlq2ibKlQo1RVRKeUtzpnvbEAKebxg/Yd5OTJGoPGwoWLeZ6FZhhRP/dI2ZBj2BetkqTdvlDGtxamOkMbHeeQ== 211 | dependencies: 212 | "@firebase/component" "0.4.1" 213 | "@firebase/storage-types" "0.4.1" 214 | "@firebase/util" "1.0.0" 215 | tslib "^2.1.0" 216 | 217 | "@firebase/util@1.0.0": 218 | version "1.0.0" 219 | resolved "https://registry.yarnpkg.com/@firebase/util/-/util-1.0.0.tgz#cbe8ec610a84a7d2fc804af31010305941f4a34b" 220 | integrity sha512-KIEyuyrYKKtit+lAl66c2GVvooM1Pb+Yw/9yuSga1HKYMxNZwSsIMXU8X97sLZf7WJaanV1XNJEMkZTw3xKEoA== 221 | dependencies: 222 | tslib "^2.1.0" 223 | 224 | "@firebase/webchannel-wrapper@0.4.1": 225 | version "0.4.1" 226 | resolved "https://registry.yarnpkg.com/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.4.1.tgz#600f2275ff54739ad5ac0102f1467b8963cd5f71" 227 | integrity sha512-0yPjzuzGMkW1GkrC8yWsiN7vt1OzkMIi9HgxRmKREZl2wnNPOKo/yScTjXf/O57HM8dltqxPF6jlNLFVtc2qdw== 228 | 229 | "@grpc/grpc-js@^1.0.0": 230 | version "1.2.12" 231 | resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.12.tgz#0153f27512acf69184bb52c0a1035ca91d6c14b0" 232 | integrity sha512-+gPCklP1eqIgrNPyzddYQdt9+GvZqPlLpIjIo+TveE+gbtp74VV1A2ju8ExeO8ma8f7MbpaGZx/KJPYVWL9eDw== 233 | dependencies: 234 | "@types/node" ">=12.12.47" 235 | google-auth-library "^6.1.1" 236 | semver "^6.2.0" 237 | 238 | "@grpc/proto-loader@^0.5.0": 239 | version "0.5.6" 240 | resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.6.tgz#1dea4b8a6412b05e2d58514d507137b63a52a98d" 241 | integrity sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ== 242 | dependencies: 243 | lodash.camelcase "^4.3.0" 244 | protobufjs "^6.8.6" 245 | 246 | "@now/node@^1.0.0": 247 | version "1.0.1" 248 | resolved "https://registry.yarnpkg.com/@now/node/-/node-1.0.1.tgz#519e137a7b0da913ee409d1613ecab8b8d0e4a8c" 249 | integrity sha512-unClfFk4s7q31AyE6D+8LjQTeFRG+YKfeCCMXT+/KXIuCzCx/uqCtlP/rp9sCdxayVM93iPg9x1Ttbn3ySg7zQ== 250 | dependencies: 251 | "@types/node" "*" 252 | 253 | "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": 254 | version "1.1.2" 255 | resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" 256 | integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= 257 | 258 | "@protobufjs/base64@^1.1.2": 259 | version "1.1.2" 260 | resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" 261 | integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== 262 | 263 | "@protobufjs/codegen@^2.0.4": 264 | version "2.0.4" 265 | resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" 266 | integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== 267 | 268 | "@protobufjs/eventemitter@^1.1.0": 269 | version "1.1.0" 270 | resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" 271 | integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= 272 | 273 | "@protobufjs/fetch@^1.1.0": 274 | version "1.1.0" 275 | resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" 276 | integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= 277 | dependencies: 278 | "@protobufjs/aspromise" "^1.1.1" 279 | "@protobufjs/inquire" "^1.1.0" 280 | 281 | "@protobufjs/float@^1.0.2": 282 | version "1.0.2" 283 | resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" 284 | integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= 285 | 286 | "@protobufjs/inquire@^1.1.0": 287 | version "1.1.0" 288 | resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" 289 | integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= 290 | 291 | "@protobufjs/path@^1.1.2": 292 | version "1.1.2" 293 | resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" 294 | integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= 295 | 296 | "@protobufjs/pool@^1.1.0": 297 | version "1.1.0" 298 | resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" 299 | integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= 300 | 301 | "@protobufjs/utf8@^1.1.0": 302 | version "1.1.0" 303 | resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" 304 | integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= 305 | 306 | "@sentry/cli@^1.48.0": 307 | version "1.48.0" 308 | resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.48.0.tgz#cccf2badd3fab9aeaafeaf6ef21bc74e2f6b6790" 309 | integrity sha512-Td6UpZP6BoI0R5Guctiq3NIphAfskbyeiboWx4H1yuXPA9p4F0hL+Hrz6k3NNfzkt3AQzEkeAhvpKmIfmUOkjA== 310 | dependencies: 311 | fs-copy-file-sync "^1.1.1" 312 | https-proxy-agent "^3.0.0" 313 | mkdirp "^0.5.1" 314 | node-fetch "^2.1.2" 315 | progress "2.0.0" 316 | proxy-from-env "^1.0.0" 317 | 318 | "@sentry/core@5.7.1": 319 | version "5.7.1" 320 | resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.7.1.tgz#3eb2b7662cac68245931ee939ec809bf7a639d0e" 321 | integrity sha512-AOn3k3uVWh2VyajcHbV9Ta4ieDIeLckfo7UMLM+CTk2kt7C89SayDGayJMSsIrsZlL4qxBoLB9QY4W2FgAGJrg== 322 | dependencies: 323 | "@sentry/hub" "5.7.1" 324 | "@sentry/minimal" "5.7.1" 325 | "@sentry/types" "5.7.1" 326 | "@sentry/utils" "5.7.1" 327 | tslib "^1.9.3" 328 | 329 | "@sentry/hub@5.7.1": 330 | version "5.7.1" 331 | resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.7.1.tgz#a52acd9fead7f3779d96e9965c6978aecc8b9cad" 332 | integrity sha512-evGh323WR073WSBCg/RkhlUmCQyzU0xzBzCZPscvcoy5hd4SsLE6t9Zin+WACHB9JFsRQIDwNDn+D+pj3yKsig== 333 | dependencies: 334 | "@sentry/types" "5.7.1" 335 | "@sentry/utils" "5.7.1" 336 | tslib "^1.9.3" 337 | 338 | "@sentry/minimal@5.7.1": 339 | version "5.7.1" 340 | resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.7.1.tgz#56afc537737586929e25349765e37a367958c1e1" 341 | integrity sha512-nS/Dg+jWAZtcxQW8wKbkkw4dYvF6uyY/vDiz/jFCaux0LX0uhgXAC9gMOJmgJ/tYBLJ64l0ca5LzpZa7BMJQ0g== 342 | dependencies: 343 | "@sentry/hub" "5.7.1" 344 | "@sentry/types" "5.7.1" 345 | tslib "^1.9.3" 346 | 347 | "@sentry/node@^5.7.1": 348 | version "5.7.1" 349 | resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.7.1.tgz#94e2fbac94f6cc061be3bc14b22813536c59698d" 350 | integrity sha512-hVM10asFStrOhYZzMqFM7V1lrHkr1ydc2n/SFG0ZmIQxfTjCVElyXV/BJASIdqadM1fFIvvtD/EfgkTcZmub1g== 351 | dependencies: 352 | "@sentry/core" "5.7.1" 353 | "@sentry/hub" "5.7.1" 354 | "@sentry/types" "5.7.1" 355 | "@sentry/utils" "5.7.1" 356 | cookie "^0.3.1" 357 | https-proxy-agent "^3.0.0" 358 | lru_map "^0.3.3" 359 | tslib "^1.9.3" 360 | 361 | "@sentry/types@5.7.1": 362 | version "5.7.1" 363 | resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.7.1.tgz#4c4c1d4d891b6b8c2c3c7b367d306a8b1350f090" 364 | integrity sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ== 365 | 366 | "@sentry/utils@5.7.1": 367 | version "5.7.1" 368 | resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.7.1.tgz#cf37ad55f78e317665cd8680f202d307fa77f1d0" 369 | integrity sha512-nhirUKj/qFLsR1i9kJ5BRvNyzdx/E2vorIsukuDrbo8e3iZ11JMgCOVrmC8Eq9YkHBqgwX4UnrPumjFyvGMZ2Q== 370 | dependencies: 371 | "@sentry/types" "5.7.1" 372 | tslib "^1.9.3" 373 | 374 | "@types/http-proxy@^1.17.0": 375 | version "1.17.0" 376 | resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.0.tgz#baf82ff6aa2723fd29f90e3ba1384e665006863e" 377 | integrity sha512-l+s0IoxSHqhLFJPDHRfO235kgrCkvFD8JmdV/T9C4BKBYPIjrQopGFH4r7h2e3jQqgJRCthRCAZIxDoFnj1zwQ== 378 | dependencies: 379 | "@types/node" "*" 380 | 381 | "@types/long@^4.0.1": 382 | version "4.0.1" 383 | resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" 384 | integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== 385 | 386 | "@types/node@*": 387 | version "12.7.12" 388 | resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.12.tgz#7c6c571cc2f3f3ac4a59a5f2bd48f5bdbc8653cc" 389 | integrity sha512-KPYGmfD0/b1eXurQ59fXD1GBzhSQfz6/lKBxkaHX9dKTzjXbK68Zt7yGUxUsCS1jeTy/8aL+d9JEr+S54mpkWQ== 390 | 391 | "@types/node@>=12.12.47": 392 | version "14.14.41" 393 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.41.tgz#d0b939d94c1d7bd53d04824af45f1139b8c45615" 394 | integrity sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g== 395 | 396 | "@types/node@^13.7.0": 397 | version "13.13.50" 398 | resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.50.tgz#bc8ebf70c392a98ffdba7aab9b46989ea96c1c62" 399 | integrity sha512-y7kkh+hX/0jZNxMyBR/6asG0QMSaPSzgeVK63dhWHl4QAXCQB8lExXmzLL6SzmOgKHydtawpMnNhlDbv7DXPEA== 400 | 401 | "@types/uuid@^3.4.5": 402 | version "3.4.5" 403 | resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.5.tgz#d4dc10785b497a1474eae0ba7f0cb09c0ddfd6eb" 404 | integrity sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA== 405 | dependencies: 406 | "@types/node" "*" 407 | 408 | abort-controller@^3.0.0: 409 | version "3.0.0" 410 | resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" 411 | integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== 412 | dependencies: 413 | event-target-shim "^5.0.0" 414 | 415 | agent-base@6: 416 | version "6.0.2" 417 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" 418 | integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== 419 | dependencies: 420 | debug "4" 421 | 422 | agent-base@^4.3.0: 423 | version "4.3.0" 424 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" 425 | integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== 426 | dependencies: 427 | es6-promisify "^5.0.0" 428 | 429 | arrify@^2.0.0: 430 | version "2.0.1" 431 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" 432 | integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== 433 | 434 | base64-js@^1.3.0: 435 | version "1.5.1" 436 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 437 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 438 | 439 | bignumber.js@^9.0.0: 440 | version "9.0.1" 441 | resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" 442 | integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== 443 | 444 | buffer-equal-constant-time@1.0.1: 445 | version "1.0.1" 446 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" 447 | integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= 448 | 449 | cookie@^0.3.1: 450 | version "0.3.1" 451 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 452 | integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= 453 | 454 | core-js@3.6.5: 455 | version "3.6.5" 456 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" 457 | integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== 458 | 459 | debug@4: 460 | version "4.3.1" 461 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" 462 | integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== 463 | dependencies: 464 | ms "2.1.2" 465 | 466 | debug@^3.0.0, debug@^3.1.0: 467 | version "3.2.6" 468 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 469 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 470 | dependencies: 471 | ms "^2.1.1" 472 | 473 | dom-storage@2.1.0: 474 | version "2.1.0" 475 | resolved "https://registry.yarnpkg.com/dom-storage/-/dom-storage-2.1.0.tgz#00fb868bc9201357ea243c7bcfd3304c1e34ea39" 476 | integrity sha512-g6RpyWXzl0RR6OTElHKBl7nwnK87GUyZMYC7JWsB/IA73vpqK2K6LT39x4VepLxlSsWBFrPVLnsSR5Jyty0+2Q== 477 | 478 | ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: 479 | version "1.0.11" 480 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" 481 | integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== 482 | dependencies: 483 | safe-buffer "^5.0.1" 484 | 485 | es6-promise@^4.0.3: 486 | version "4.2.8" 487 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" 488 | integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== 489 | 490 | es6-promisify@^5.0.0: 491 | version "5.0.0" 492 | resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" 493 | integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= 494 | dependencies: 495 | es6-promise "^4.0.3" 496 | 497 | event-target-shim@^5.0.0: 498 | version "5.0.1" 499 | resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" 500 | integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== 501 | 502 | eventemitter3@^4.0.0: 503 | version "4.0.0" 504 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" 505 | integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== 506 | 507 | extend@^3.0.2: 508 | version "3.0.2" 509 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" 510 | integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== 511 | 512 | fast-text-encoding@^1.0.0: 513 | version "1.0.3" 514 | resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" 515 | integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== 516 | 517 | faye-websocket@0.11.3: 518 | version "0.11.3" 519 | resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" 520 | integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== 521 | dependencies: 522 | websocket-driver ">=0.5.1" 523 | 524 | firebase@^8.4.2: 525 | version "8.4.2" 526 | resolved "https://registry.yarnpkg.com/firebase/-/firebase-8.4.2.tgz#5b3eff63683bd7379677b79f8efaf8e2ed2be5d4" 527 | integrity sha512-PUzobCZ2z6nEvb3IRxz9L1H/q7e7LaKhx6vEUYhKTIK7WMYg+iiUE67IY4Lvh5/IWugclAH8SX5ny8azSy2hTw== 528 | dependencies: 529 | "@firebase/analytics" "0.6.9" 530 | "@firebase/app" "0.6.20" 531 | "@firebase/app-types" "0.6.2" 532 | "@firebase/auth" "0.16.5" 533 | "@firebase/database" "0.9.11" 534 | "@firebase/firestore" "2.2.5" 535 | "@firebase/functions" "0.6.7" 536 | "@firebase/installations" "0.4.25" 537 | "@firebase/messaging" "0.7.9" 538 | "@firebase/performance" "0.4.11" 539 | "@firebase/polyfill" "0.3.36" 540 | "@firebase/remote-config" "0.1.36" 541 | "@firebase/storage" "0.5.1" 542 | "@firebase/util" "1.0.0" 543 | 544 | follow-redirects@^1.0.0: 545 | version "1.9.0" 546 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" 547 | integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== 548 | dependencies: 549 | debug "^3.0.0" 550 | 551 | fs-copy-file-sync@^1.1.1: 552 | version "1.1.1" 553 | resolved "https://registry.yarnpkg.com/fs-copy-file-sync/-/fs-copy-file-sync-1.1.1.tgz#11bf32c096c10d126e5f6b36d06eece776062918" 554 | integrity sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ== 555 | 556 | gaxios@^4.0.0: 557 | version "4.2.0" 558 | resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.2.0.tgz#33bdc4fc241fc33b8915a4b8c07cfb368b932e46" 559 | integrity sha512-Ms7fNifGv0XVU+6eIyL9LB7RVESeML9+cMvkwGS70xyD6w2Z80wl6RiqiJ9k1KFlJCUTQqFFc8tXmPQfSKUe8g== 560 | dependencies: 561 | abort-controller "^3.0.0" 562 | extend "^3.0.2" 563 | https-proxy-agent "^5.0.0" 564 | is-stream "^2.0.0" 565 | node-fetch "^2.3.0" 566 | 567 | gcp-metadata@^4.2.0: 568 | version "4.2.1" 569 | resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-4.2.1.tgz#31849fbcf9025ef34c2297c32a89a1e7e9f2cd62" 570 | integrity sha512-tSk+REe5iq/N+K+SK1XjZJUrFPuDqGZVzCy2vocIHIGmPlTGsa8owXMJwGkrXr73NO0AzhPW4MF2DEHz7P2AVw== 571 | dependencies: 572 | gaxios "^4.0.0" 573 | json-bigint "^1.0.0" 574 | 575 | google-auth-library@^6.1.1: 576 | version "6.1.6" 577 | resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" 578 | integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== 579 | dependencies: 580 | arrify "^2.0.0" 581 | base64-js "^1.3.0" 582 | ecdsa-sig-formatter "^1.0.11" 583 | fast-text-encoding "^1.0.0" 584 | gaxios "^4.0.0" 585 | gcp-metadata "^4.2.0" 586 | gtoken "^5.0.4" 587 | jws "^4.0.0" 588 | lru-cache "^6.0.0" 589 | 590 | google-p12-pem@^3.0.3: 591 | version "3.0.3" 592 | resolved "https://registry.yarnpkg.com/google-p12-pem/-/google-p12-pem-3.0.3.tgz#673ac3a75d3903a87f05878f3c75e06fc151669e" 593 | integrity sha512-wS0ek4ZtFx/ACKYF3JhyGe5kzH7pgiQ7J5otlumqR9psmWMYc+U9cErKlCYVYHoUaidXHdZ2xbo34kB+S+24hA== 594 | dependencies: 595 | node-forge "^0.10.0" 596 | 597 | gtoken@^5.0.4: 598 | version "5.2.1" 599 | resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16" 600 | integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw== 601 | dependencies: 602 | gaxios "^4.0.0" 603 | google-p12-pem "^3.0.3" 604 | jws "^4.0.0" 605 | 606 | "http-parser-js@>=0.4.0 <0.4.11": 607 | version "0.4.10" 608 | resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" 609 | integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= 610 | 611 | http-proxy@^1.17.0: 612 | version "1.18.0" 613 | resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" 614 | integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== 615 | dependencies: 616 | eventemitter3 "^4.0.0" 617 | follow-redirects "^1.0.0" 618 | requires-port "^1.0.0" 619 | 620 | https-proxy-agent@^3.0.0: 621 | version "3.0.0" 622 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz#0106efa5d63d6d6f3ab87c999fa4877a3fd1ff97" 623 | integrity sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ== 624 | dependencies: 625 | agent-base "^4.3.0" 626 | debug "^3.1.0" 627 | 628 | https-proxy-agent@^5.0.0: 629 | version "5.0.0" 630 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" 631 | integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== 632 | dependencies: 633 | agent-base "6" 634 | debug "4" 635 | 636 | idb@3.0.2: 637 | version "3.0.2" 638 | resolved "https://registry.yarnpkg.com/idb/-/idb-3.0.2.tgz#c8e9122d5ddd40f13b60ae665e4862f8b13fa384" 639 | integrity sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw== 640 | 641 | is-stream@^2.0.0: 642 | version "2.0.0" 643 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" 644 | integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== 645 | 646 | json-bigint@^1.0.0: 647 | version "1.0.0" 648 | resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" 649 | integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== 650 | dependencies: 651 | bignumber.js "^9.0.0" 652 | 653 | jwa@^2.0.0: 654 | version "2.0.0" 655 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.0.tgz#a7e9c3f29dae94027ebcaf49975c9345593410fc" 656 | integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== 657 | dependencies: 658 | buffer-equal-constant-time "1.0.1" 659 | ecdsa-sig-formatter "1.0.11" 660 | safe-buffer "^5.0.1" 661 | 662 | jws@^4.0.0: 663 | version "4.0.0" 664 | resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4" 665 | integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== 666 | dependencies: 667 | jwa "^2.0.0" 668 | safe-buffer "^5.0.1" 669 | 670 | lodash.camelcase@^4.3.0: 671 | version "4.3.0" 672 | resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" 673 | integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= 674 | 675 | long@^4.0.0: 676 | version "4.0.0" 677 | resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" 678 | integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== 679 | 680 | lru-cache@^6.0.0: 681 | version "6.0.0" 682 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 683 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 684 | dependencies: 685 | yallist "^4.0.0" 686 | 687 | lru_map@^0.3.3: 688 | version "0.3.3" 689 | resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" 690 | integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= 691 | 692 | minimist@0.0.8: 693 | version "0.0.8" 694 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 695 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 696 | 697 | mkdirp@^0.5.1: 698 | version "0.5.1" 699 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 700 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 701 | dependencies: 702 | minimist "0.0.8" 703 | 704 | ms@2.1.2, ms@^2.1.1: 705 | version "2.1.2" 706 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 707 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 708 | 709 | node-fetch@2.6.1, node-fetch@^2.3.0: 710 | version "2.6.1" 711 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" 712 | integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== 713 | 714 | node-fetch@^2.1.2: 715 | version "2.6.0" 716 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" 717 | integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== 718 | 719 | node-forge@^0.10.0: 720 | version "0.10.0" 721 | resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" 722 | integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== 723 | 724 | progress@2.0.0: 725 | version "2.0.0" 726 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" 727 | integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8= 728 | 729 | promise-polyfill@8.1.3: 730 | version "8.1.3" 731 | resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.1.3.tgz#8c99b3cf53f3a91c68226ffde7bde81d7f904116" 732 | integrity sha512-MG5r82wBzh7pSKDRa9y+vllNHz3e3d4CNj1PQE4BQYxLme0gKYYBm9YENq+UkEikyZ0XbiGWxYlVw3Rl9O/U8g== 733 | 734 | protobufjs@^6.8.6: 735 | version "6.10.2" 736 | resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" 737 | integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== 738 | dependencies: 739 | "@protobufjs/aspromise" "^1.1.2" 740 | "@protobufjs/base64" "^1.1.2" 741 | "@protobufjs/codegen" "^2.0.4" 742 | "@protobufjs/eventemitter" "^1.1.0" 743 | "@protobufjs/fetch" "^1.1.0" 744 | "@protobufjs/float" "^1.0.2" 745 | "@protobufjs/inquire" "^1.1.0" 746 | "@protobufjs/path" "^1.1.2" 747 | "@protobufjs/pool" "^1.1.0" 748 | "@protobufjs/utf8" "^1.1.0" 749 | "@types/long" "^4.0.1" 750 | "@types/node" "^13.7.0" 751 | long "^4.0.0" 752 | 753 | proxy-from-env@^1.0.0: 754 | version "1.0.0" 755 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" 756 | integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= 757 | 758 | requires-port@^1.0.0: 759 | version "1.0.0" 760 | resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" 761 | integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= 762 | 763 | safe-buffer@>=5.1.0: 764 | version "5.2.0" 765 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" 766 | integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== 767 | 768 | safe-buffer@^5.0.1: 769 | version "5.2.1" 770 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 771 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 772 | 773 | semver@^6.2.0: 774 | version "6.3.0" 775 | resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" 776 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== 777 | 778 | slack@^11.0.2: 779 | version "11.0.2" 780 | resolved "https://registry.yarnpkg.com/slack/-/slack-11.0.2.tgz#30f68527c5d1712b7faa3141db7716f89ac6e911" 781 | integrity sha512-rv842+S+AGyZCmMMd8xPtW5DvJ9LzWTAKfxi8Gw57oYlXgcKtFuHd4nqk6lTPpRKdUGn3tx/Drd0rjQR3dQPqw== 782 | dependencies: 783 | tiny-json-http "^7.0.2" 784 | 785 | tiny-json-http@^7.0.2: 786 | version "7.1.2" 787 | resolved "https://registry.yarnpkg.com/tiny-json-http/-/tiny-json-http-7.1.2.tgz#620e189849bab08992ec23fada7b48c7c61637b4" 788 | integrity sha512-XB9Bu+ohdQso6ziPFNVqK+pcTt0l8BSRkW/CCBq0pUVlLxcYDsorpo7ae5yPhu2CF1xYgJuKVLF7cfOGeLCTlA== 789 | 790 | tslib@^1.9.3: 791 | version "1.10.0" 792 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" 793 | integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== 794 | 795 | tslib@^2.1.0: 796 | version "2.2.0" 797 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" 798 | integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== 799 | 800 | typescript@^3.6.4: 801 | version "3.6.4" 802 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d" 803 | integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg== 804 | 805 | uuid@^3.3.2: 806 | version "3.3.3" 807 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" 808 | integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== 809 | 810 | websocket-driver@>=0.5.1: 811 | version "0.7.3" 812 | resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9" 813 | integrity sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg== 814 | dependencies: 815 | http-parser-js ">=0.4.0 <0.4.11" 816 | safe-buffer ">=5.1.0" 817 | websocket-extensions ">=0.1.1" 818 | 819 | websocket-extensions@>=0.1.1: 820 | version "0.1.4" 821 | resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" 822 | integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== 823 | 824 | whatwg-fetch@2.0.4: 825 | version "2.0.4" 826 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" 827 | integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== 828 | 829 | xmlhttprequest@1.8.0: 830 | version "1.8.0" 831 | resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" 832 | integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= 833 | 834 | yallist@^4.0.0: 835 | version "4.0.0" 836 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 837 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 838 | --------------------------------------------------------------------------------