api: revamp tests
Parents:
86b807213 file(s) changed
- api/package-lock.json +1501 -0
- api/package.json +5 -3
- api/tests/database-auth.test.ts +254 -0
- api/tests/mood.test.js +0 -98
- api/tests/mood/creation.test.js +0 -498
- api/tests/mood/queries.test.js +0 -298
- api/tests/mood/updates.test.js +0 -349
- api/tests/test-helper.js +0 -33
- api/tests/test-helper.ts +52 -0
- api/tests/users.test.js +0 -68
- api/tests/users/authentication.test.js +0 -147
- api/tests/users/creation.test.js +0 -257
- api/tests/users/profile.test.js +0 -246
api/package-lock.json
@@ -30,6 +30,8 @@ "@types/express": "^5.0.6",
30 30 "@types/morgan": "^1.9.10",
31 31 "@types/node": "^25.5.2",
32 32 "ava": "^6.4.1",
33 + "cross-var": "^1.1.0",
34 + "dotenv-cli": "^11.0.0",
33 35 "prisma": "^7.5.0",
34 36 "supertest": "^7.1.4",
35 37 "ts-node": "^10.9.2",
@@ -1546,6 +1548,1042 @@ "engines": {
1546 1548 "node": ">= 6.0.0"
1547 1549 }
1548 1550 },
1551 + "node_modules/babel-code-frame": {
1552 + "version": "6.26.0",
1553 + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
1554 + "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
1555 + "dev": true,
1556 + "license": "MIT",
1557 + "dependencies": {
1558 + "chalk": "^1.1.3",
1559 + "esutils": "^2.0.2",
1560 + "js-tokens": "^3.0.2"
1561 + }
1562 + },
1563 + "node_modules/babel-code-frame/node_modules/ansi-regex": {
1564 + "version": "2.1.1",
1565 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
1566 + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
1567 + "dev": true,
1568 + "license": "MIT",
1569 + "engines": {
1570 + "node": ">=0.10.0"
1571 + }
1572 + },
1573 + "node_modules/babel-code-frame/node_modules/ansi-styles": {
1574 + "version": "2.2.1",
1575 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
1576 + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
1577 + "dev": true,
1578 + "license": "MIT",
1579 + "engines": {
1580 + "node": ">=0.10.0"
1581 + }
1582 + },
1583 + "node_modules/babel-code-frame/node_modules/chalk": {
1584 + "version": "1.1.3",
1585 + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
1586 + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
1587 + "dev": true,
1588 + "license": "MIT",
1589 + "dependencies": {
1590 + "ansi-styles": "^2.2.1",
1591 + "escape-string-regexp": "^1.0.2",
1592 + "has-ansi": "^2.0.0",
1593 + "strip-ansi": "^3.0.0",
1594 + "supports-color": "^2.0.0"
1595 + },
1596 + "engines": {
1597 + "node": ">=0.10.0"
1598 + }
1599 + },
1600 + "node_modules/babel-code-frame/node_modules/escape-string-regexp": {
1601 + "version": "1.0.5",
1602 + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
1603 + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
1604 + "dev": true,
1605 + "license": "MIT",
1606 + "engines": {
1607 + "node": ">=0.8.0"
1608 + }
1609 + },
1610 + "node_modules/babel-code-frame/node_modules/strip-ansi": {
1611 + "version": "3.0.1",
1612 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
1613 + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
1614 + "dev": true,
1615 + "license": "MIT",
1616 + "dependencies": {
1617 + "ansi-regex": "^2.0.0"
1618 + },
1619 + "engines": {
1620 + "node": ">=0.10.0"
1621 + }
1622 + },
1623 + "node_modules/babel-core": {
1624 + "version": "6.26.3",
1625 + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
1626 + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
1627 + "dev": true,
1628 + "license": "MIT",
1629 + "dependencies": {
1630 + "babel-code-frame": "^6.26.0",
1631 + "babel-generator": "^6.26.0",
1632 + "babel-helpers": "^6.24.1",
1633 + "babel-messages": "^6.23.0",
1634 + "babel-register": "^6.26.0",
1635 + "babel-runtime": "^6.26.0",
1636 + "babel-template": "^6.26.0",
1637 + "babel-traverse": "^6.26.0",
1638 + "babel-types": "^6.26.0",
1639 + "babylon": "^6.18.0",
1640 + "convert-source-map": "^1.5.1",
1641 + "debug": "^2.6.9",
1642 + "json5": "^0.5.1",
1643 + "lodash": "^4.17.4",
1644 + "minimatch": "^3.0.4",
1645 + "path-is-absolute": "^1.0.1",
1646 + "private": "^0.1.8",
1647 + "slash": "^1.0.0",
1648 + "source-map": "^0.5.7"
1649 + }
1650 + },
1651 + "node_modules/babel-core/node_modules/brace-expansion": {
1652 + "version": "1.1.13",
1653 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
1654 + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
1655 + "dev": true,
1656 + "license": "MIT",
1657 + "dependencies": {
1658 + "balanced-match": "^1.0.0",
1659 + "concat-map": "0.0.1"
1660 + }
1661 + },
1662 + "node_modules/babel-core/node_modules/debug": {
1663 + "version": "2.6.9",
1664 + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1665 + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1666 + "dev": true,
1667 + "license": "MIT",
1668 + "dependencies": {
1669 + "ms": "2.0.0"
1670 + }
1671 + },
1672 + "node_modules/babel-core/node_modules/json5": {
1673 + "version": "0.5.1",
1674 + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
1675 + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==",
1676 + "dev": true,
1677 + "license": "MIT",
1678 + "bin": {
1679 + "json5": "lib/cli.js"
1680 + }
1681 + },
1682 + "node_modules/babel-core/node_modules/minimatch": {
1683 + "version": "3.1.5",
1684 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
1685 + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
1686 + "dev": true,
1687 + "license": "ISC",
1688 + "dependencies": {
1689 + "brace-expansion": "^1.1.7"
1690 + },
1691 + "engines": {
1692 + "node": "*"
1693 + }
1694 + },
1695 + "node_modules/babel-core/node_modules/ms": {
1696 + "version": "2.0.0",
1697 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1698 + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1699 + "dev": true,
1700 + "license": "MIT"
1701 + },
1702 + "node_modules/babel-core/node_modules/slash": {
1703 + "version": "1.0.0",
1704 + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
1705 + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==",
1706 + "dev": true,
1707 + "license": "MIT",
1708 + "engines": {
1709 + "node": ">=0.10.0"
1710 + }
1711 + },
1712 + "node_modules/babel-generator": {
1713 + "version": "6.26.1",
1714 + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
1715 + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
1716 + "dev": true,
1717 + "license": "MIT",
1718 + "dependencies": {
1719 + "babel-messages": "^6.23.0",
1720 + "babel-runtime": "^6.26.0",
1721 + "babel-types": "^6.26.0",
1722 + "detect-indent": "^4.0.0",
1723 + "jsesc": "^1.3.0",
1724 + "lodash": "^4.17.4",
1725 + "source-map": "^0.5.7",
1726 + "trim-right": "^1.0.1"
1727 + }
1728 + },
1729 + "node_modules/babel-helper-bindify-decorators": {
1730 + "version": "6.24.1",
1731 + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz",
1732 + "integrity": "sha512-TYX2QQATKA6Wssp6j7jqlw4QLmABDN1olRdEHndYvBXdaXM5dcx6j5rN0+nd+aVL+Th40fAEYvvw/Xxd/LETuQ==",
1733 + "dev": true,
1734 + "license": "MIT",
1735 + "dependencies": {
1736 + "babel-runtime": "^6.22.0",
1737 + "babel-traverse": "^6.24.1",
1738 + "babel-types": "^6.24.1"
1739 + }
1740 + },
1741 + "node_modules/babel-helper-builder-binary-assignment-operator-visitor": {
1742 + "version": "6.24.1",
1743 + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
1744 + "integrity": "sha512-gCtfYORSG1fUMX4kKraymq607FWgMWg+j42IFPc18kFQEsmtaibP4UrqsXt8FlEJle25HUd4tsoDR7H2wDhe9Q==",
1745 + "dev": true,
1746 + "license": "MIT",
1747 + "dependencies": {
1748 + "babel-helper-explode-assignable-expression": "^6.24.1",
1749 + "babel-runtime": "^6.22.0",
1750 + "babel-types": "^6.24.1"
1751 + }
1752 + },
1753 + "node_modules/babel-helper-call-delegate": {
1754 + "version": "6.24.1",
1755 + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
1756 + "integrity": "sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==",
1757 + "dev": true,
1758 + "license": "MIT",
1759 + "dependencies": {
1760 + "babel-helper-hoist-variables": "^6.24.1",
1761 + "babel-runtime": "^6.22.0",
1762 + "babel-traverse": "^6.24.1",
1763 + "babel-types": "^6.24.1"
1764 + }
1765 + },
1766 + "node_modules/babel-helper-define-map": {
1767 + "version": "6.26.0",
1768 + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
1769 + "integrity": "sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==",
1770 + "dev": true,
1771 + "license": "MIT",
1772 + "dependencies": {
1773 + "babel-helper-function-name": "^6.24.1",
1774 + "babel-runtime": "^6.26.0",
1775 + "babel-types": "^6.26.0",
1776 + "lodash": "^4.17.4"
1777 + }
1778 + },
1779 + "node_modules/babel-helper-explode-assignable-expression": {
1780 + "version": "6.24.1",
1781 + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
1782 + "integrity": "sha512-qe5csbhbvq6ccry9G7tkXbzNtcDiH4r51rrPUbwwoTzZ18AqxWYRZT6AOmxrpxKnQBW0pYlBI/8vh73Z//78nQ==",
1783 + "dev": true,
1784 + "license": "MIT",
1785 + "dependencies": {
1786 + "babel-runtime": "^6.22.0",
1787 + "babel-traverse": "^6.24.1",
1788 + "babel-types": "^6.24.1"
1789 + }
1790 + },
1791 + "node_modules/babel-helper-explode-class": {
1792 + "version": "6.24.1",
1793 + "resolved": "https://registry.npmjs.org/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz",
1794 + "integrity": "sha512-SFbWewr0/0U4AiRzsHqwsbOQeLXVa9T1ELdqEa2efcQB5KopTnunAqoj07TuHlN2lfTQNPGO/rJR4FMln5fVcA==",
1795 + "dev": true,
1796 + "license": "MIT",
1797 + "dependencies": {
1798 + "babel-helper-bindify-decorators": "^6.24.1",
1799 + "babel-runtime": "^6.22.0",
1800 + "babel-traverse": "^6.24.1",
1801 + "babel-types": "^6.24.1"
1802 + }
1803 + },
1804 + "node_modules/babel-helper-function-name": {
1805 + "version": "6.24.1",
1806 + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
1807 + "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==",
1808 + "dev": true,
1809 + "license": "MIT",
1810 + "dependencies": {
1811 + "babel-helper-get-function-arity": "^6.24.1",
1812 + "babel-runtime": "^6.22.0",
1813 + "babel-template": "^6.24.1",
1814 + "babel-traverse": "^6.24.1",
1815 + "babel-types": "^6.24.1"
1816 + }
1817 + },
1818 + "node_modules/babel-helper-get-function-arity": {
1819 + "version": "6.24.1",
1820 + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
1821 + "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==",
1822 + "dev": true,
1823 + "license": "MIT",
1824 + "dependencies": {
1825 + "babel-runtime": "^6.22.0",
1826 + "babel-types": "^6.24.1"
1827 + }
1828 + },
1829 + "node_modules/babel-helper-hoist-variables": {
1830 + "version": "6.24.1",
1831 + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
1832 + "integrity": "sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==",
1833 + "dev": true,
1834 + "license": "MIT",
1835 + "dependencies": {
1836 + "babel-runtime": "^6.22.0",
1837 + "babel-types": "^6.24.1"
1838 + }
1839 + },
1840 + "node_modules/babel-helper-optimise-call-expression": {
1841 + "version": "6.24.1",
1842 + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
1843 + "integrity": "sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==",
1844 + "dev": true,
1845 + "license": "MIT",
1846 + "dependencies": {
1847 + "babel-runtime": "^6.22.0",
1848 + "babel-types": "^6.24.1"
1849 + }
1850 + },
1851 + "node_modules/babel-helper-regex": {
1852 + "version": "6.26.0",
1853 + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
1854 + "integrity": "sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==",
1855 + "dev": true,
1856 + "license": "MIT",
1857 + "dependencies": {
1858 + "babel-runtime": "^6.26.0",
1859 + "babel-types": "^6.26.0",
1860 + "lodash": "^4.17.4"
1861 + }
1862 + },
1863 + "node_modules/babel-helper-remap-async-to-generator": {
1864 + "version": "6.24.1",
1865 + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
1866 + "integrity": "sha512-RYqaPD0mQyQIFRu7Ho5wE2yvA/5jxqCIj/Lv4BXNq23mHYu/vxikOy2JueLiBxQknwapwrJeNCesvY0ZcfnlHg==",
1867 + "dev": true,
1868 + "license": "MIT",
1869 + "dependencies": {
1870 + "babel-helper-function-name": "^6.24.1",
1871 + "babel-runtime": "^6.22.0",
1872 + "babel-template": "^6.24.1",
1873 + "babel-traverse": "^6.24.1",
1874 + "babel-types": "^6.24.1"
1875 + }
1876 + },
1877 + "node_modules/babel-helper-replace-supers": {
1878 + "version": "6.24.1",
1879 + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
1880 + "integrity": "sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==",
1881 + "dev": true,
1882 + "license": "MIT",
1883 + "dependencies": {
1884 + "babel-helper-optimise-call-expression": "^6.24.1",
1885 + "babel-messages": "^6.23.0",
1886 + "babel-runtime": "^6.22.0",
1887 + "babel-template": "^6.24.1",
1888 + "babel-traverse": "^6.24.1",
1889 + "babel-types": "^6.24.1"
1890 + }
1891 + },
1892 + "node_modules/babel-helpers": {
1893 + "version": "6.24.1",
1894 + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
1895 + "integrity": "sha512-n7pFrqQm44TCYvrCDb0MqabAF+JUBq+ijBvNMUxpkLjJaAu32faIexewMumrH5KLLJ1HDyT0PTEqRyAe/GwwuQ==",
1896 + "dev": true,
1897 + "license": "MIT",
1898 + "dependencies": {
1899 + "babel-runtime": "^6.22.0",
1900 + "babel-template": "^6.24.1"
1901 + }
1902 + },
1903 + "node_modules/babel-messages": {
1904 + "version": "6.23.0",
1905 + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
1906 + "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==",
1907 + "dev": true,
1908 + "license": "MIT",
1909 + "dependencies": {
1910 + "babel-runtime": "^6.22.0"
1911 + }
1912 + },
1913 + "node_modules/babel-plugin-check-es2015-constants": {
1914 + "version": "6.22.0",
1915 + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
1916 + "integrity": "sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==",
1917 + "dev": true,
1918 + "license": "MIT",
1919 + "dependencies": {
1920 + "babel-runtime": "^6.22.0"
1921 + }
1922 + },
1923 + "node_modules/babel-plugin-syntax-async-functions": {
1924 + "version": "6.13.0",
1925 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
1926 + "integrity": "sha512-4Zp4unmHgw30A1eWI5EpACji2qMocisdXhAftfhXoSV9j0Tvj6nRFE3tOmRY912E0FMRm/L5xWE7MGVT2FoLnw==",
1927 + "dev": true,
1928 + "license": "MIT"
1929 + },
1930 + "node_modules/babel-plugin-syntax-async-generators": {
1931 + "version": "6.13.0",
1932 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz",
1933 + "integrity": "sha512-EbciFN5Jb9iqU9bqaLmmFLx2G8pAUsvpWJ6OzOWBNrSY9qTohXj+7YfZx6Ug1Qqh7tCb1EA7Jvn9bMC1HBiucg==",
1934 + "dev": true,
1935 + "license": "MIT"
1936 + },
1937 + "node_modules/babel-plugin-syntax-class-constructor-call": {
1938 + "version": "6.18.0",
1939 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz",
1940 + "integrity": "sha512-EEuBcXz/wZ81Jaac0LnMHtD4Mfz9XWn2oH2Xj+CHwz2SZWUqqdtR2BgWPSdTGMmxN/5KLSh4PImt9+9ZedDarA==",
1941 + "dev": true,
1942 + "license": "MIT"
1943 + },
1944 + "node_modules/babel-plugin-syntax-class-properties": {
1945 + "version": "6.13.0",
1946 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
1947 + "integrity": "sha512-chI3Rt9T1AbrQD1s+vxw3KcwC9yHtF621/MacuItITfZX344uhQoANjpoSJZleAmW2tjlolqB/f+h7jIqXa7pA==",
1948 + "dev": true,
1949 + "license": "MIT"
1950 + },
1951 + "node_modules/babel-plugin-syntax-decorators": {
1952 + "version": "6.13.0",
1953 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz",
1954 + "integrity": "sha512-AWj19x2aDm8qFQ5O2JcD6pwJDW1YdcnO+1b81t7gxrGjz5VHiUqeYWAR4h7zueWMalRelrQDXprv2FrY1dbpbw==",
1955 + "dev": true,
1956 + "license": "MIT"
1957 + },
1958 + "node_modules/babel-plugin-syntax-do-expressions": {
1959 + "version": "6.13.0",
1960 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz",
1961 + "integrity": "sha512-HD/5qJB9oSXzl0caxM+aRD7ENICXqcc3Up/8toDQk7zNIDE7TzsqtxC5f4t9Rwhu2Ya8l9l4j6b3vOsy+a6qxg==",
1962 + "dev": true,
1963 + "license": "MIT"
1964 + },
1965 + "node_modules/babel-plugin-syntax-dynamic-import": {
1966 + "version": "6.18.0",
1967 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
1968 + "integrity": "sha512-MioUE+LfjCEz65Wf7Z/Rm4XCP5k2c+TbMd2Z2JKc7U9uwjBhAfNPE48KC4GTGKhppMeYVepwDBNO/nGY6NYHBA==",
1969 + "dev": true,
1970 + "license": "MIT"
1971 + },
1972 + "node_modules/babel-plugin-syntax-exponentiation-operator": {
1973 + "version": "6.13.0",
1974 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
1975 + "integrity": "sha512-Z/flU+T9ta0aIEKl1tGEmN/pZiI1uXmCiGFRegKacQfEJzp7iNsKloZmyJlQr+75FCJtiFfGIK03SiCvCt9cPQ==",
1976 + "dev": true,
1977 + "license": "MIT"
1978 + },
1979 + "node_modules/babel-plugin-syntax-export-extensions": {
1980 + "version": "6.13.0",
1981 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz",
1982 + "integrity": "sha512-Eo0rcRaIDMld/W6mVhePiudIuLW+Cr/8eveW3mBREfZORScZgx4rh6BAPyvzdEc/JZvQ+LkC80t0VGFs6FX+lg==",
1983 + "dev": true,
1984 + "license": "MIT"
1985 + },
1986 + "node_modules/babel-plugin-syntax-function-bind": {
1987 + "version": "6.13.0",
1988 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz",
1989 + "integrity": "sha512-m8yMoh9LIiNyeLdQs5I9G+3YXo4nqVsKQkk7YplrG4qAFbNi9hkZlow8HDHxhH9QOVFPHmy8+03NzRCdyChIKw==",
1990 + "dev": true,
1991 + "license": "MIT"
1992 + },
1993 + "node_modules/babel-plugin-syntax-object-rest-spread": {
1994 + "version": "6.13.0",
1995 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
1996 + "integrity": "sha512-C4Aq+GaAj83pRQ0EFgTvw5YO6T3Qz2KGrNRwIj9mSoNHVvdZY4KO2uA6HNtNXCw993iSZnckY1aLW8nOi8i4+w==",
1997 + "dev": true,
1998 + "license": "MIT"
1999 + },
2000 + "node_modules/babel-plugin-syntax-trailing-function-commas": {
2001 + "version": "6.22.0",
2002 + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
2003 + "integrity": "sha512-Gx9CH3Q/3GKbhs07Bszw5fPTlU+ygrOGfAhEt7W2JICwufpC4SuO0mG0+4NykPBSYPMJhqvVlDBU17qB1D+hMQ==",
2004 + "dev": true,
2005 + "license": "MIT"
2006 + },
2007 + "node_modules/babel-plugin-transform-async-generator-functions": {
2008 + "version": "6.24.1",
2009 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz",
2010 + "integrity": "sha512-uT7eovUxtXe8Q2ufcjRuJIOL0hg6VAUJhiWJBLxH/evYAw+aqoJLcYTR8hqx13iOx/FfbCMHgBmXWZjukbkyPg==",
2011 + "dev": true,
2012 + "license": "MIT",
2013 + "dependencies": {
2014 + "babel-helper-remap-async-to-generator": "^6.24.1",
2015 + "babel-plugin-syntax-async-generators": "^6.5.0",
2016 + "babel-runtime": "^6.22.0"
2017 + }
2018 + },
2019 + "node_modules/babel-plugin-transform-async-to-generator": {
2020 + "version": "6.24.1",
2021 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
2022 + "integrity": "sha512-7BgYJujNCg0Ti3x0c/DL3tStvnKS6ktIYOmo9wginv/dfZOrbSZ+qG4IRRHMBOzZ5Awb1skTiAsQXg/+IWkZYw==",
2023 + "dev": true,
2024 + "license": "MIT",
2025 + "dependencies": {
2026 + "babel-helper-remap-async-to-generator": "^6.24.1",
2027 + "babel-plugin-syntax-async-functions": "^6.8.0",
2028 + "babel-runtime": "^6.22.0"
2029 + }
2030 + },
2031 + "node_modules/babel-plugin-transform-class-constructor-call": {
2032 + "version": "6.24.1",
2033 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz",
2034 + "integrity": "sha512-RvYukT1Nh7njz8P8326ztpQUGCKwmjgu6aRIx1lkvylWITYcskg29vy1Kp8WXIq7FvhXsz0Crf2kS94bjB690A==",
2035 + "dev": true,
2036 + "license": "MIT",
2037 + "dependencies": {
2038 + "babel-plugin-syntax-class-constructor-call": "^6.18.0",
2039 + "babel-runtime": "^6.22.0",
2040 + "babel-template": "^6.24.1"
2041 + }
2042 + },
2043 + "node_modules/babel-plugin-transform-class-properties": {
2044 + "version": "6.24.1",
2045 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
2046 + "integrity": "sha512-n4jtBA3OYBdvG5PRMKsMXJXHfLYw/ZOmtxCLOOwz6Ro5XlrColkStLnz1AS1L2yfPA9BKJ1ZNlmVCLjAL9DSIg==",
2047 + "dev": true,
2048 + "license": "MIT",
2049 + "dependencies": {
2050 + "babel-helper-function-name": "^6.24.1",
2051 + "babel-plugin-syntax-class-properties": "^6.8.0",
2052 + "babel-runtime": "^6.22.0",
2053 + "babel-template": "^6.24.1"
2054 + }
2055 + },
2056 + "node_modules/babel-plugin-transform-decorators": {
2057 + "version": "6.24.1",
2058 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz",
2059 + "integrity": "sha512-skQ2CImwDkCHu0mkWvCOlBCpBIHW4/49IZWVwV4A/EnWjL9bB6UBvLyMNe3Td5XDStSZNhe69j4bfEW8dvUbew==",
2060 + "dev": true,
2061 + "license": "MIT",
2062 + "dependencies": {
2063 + "babel-helper-explode-class": "^6.24.1",
2064 + "babel-plugin-syntax-decorators": "^6.13.0",
2065 + "babel-runtime": "^6.22.0",
2066 + "babel-template": "^6.24.1",
2067 + "babel-types": "^6.24.1"
2068 + }
2069 + },
2070 + "node_modules/babel-plugin-transform-do-expressions": {
2071 + "version": "6.22.0",
2072 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz",
2073 + "integrity": "sha512-yQwYqYg+Tnj1InA8W1rsItsZVhkv1Euc4KVua9ledtPz5PDWYz7LVyy6rDBpVYUWFZj5k6GUm3YZpCbIm8Tqew==",
2074 + "dev": true,
2075 + "license": "MIT",
2076 + "dependencies": {
2077 + "babel-plugin-syntax-do-expressions": "^6.8.0",
2078 + "babel-runtime": "^6.22.0"
2079 + }
2080 + },
2081 + "node_modules/babel-plugin-transform-es2015-arrow-functions": {
2082 + "version": "6.22.0",
2083 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
2084 + "integrity": "sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==",
2085 + "dev": true,
2086 + "license": "MIT",
2087 + "dependencies": {
2088 + "babel-runtime": "^6.22.0"
2089 + }
2090 + },
2091 + "node_modules/babel-plugin-transform-es2015-block-scoped-functions": {
2092 + "version": "6.22.0",
2093 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
2094 + "integrity": "sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==",
2095 + "dev": true,
2096 + "license": "MIT",
2097 + "dependencies": {
2098 + "babel-runtime": "^6.22.0"
2099 + }
2100 + },
2101 + "node_modules/babel-plugin-transform-es2015-block-scoping": {
2102 + "version": "6.26.0",
2103 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
2104 + "integrity": "sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==",
2105 + "dev": true,
2106 + "license": "MIT",
2107 + "dependencies": {
2108 + "babel-runtime": "^6.26.0",
2109 + "babel-template": "^6.26.0",
2110 + "babel-traverse": "^6.26.0",
2111 + "babel-types": "^6.26.0",
2112 + "lodash": "^4.17.4"
2113 + }
2114 + },
2115 + "node_modules/babel-plugin-transform-es2015-classes": {
2116 + "version": "6.24.1",
2117 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
2118 + "integrity": "sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==",
2119 + "dev": true,
2120 + "license": "MIT",
2121 + "dependencies": {
2122 + "babel-helper-define-map": "^6.24.1",
2123 + "babel-helper-function-name": "^6.24.1",
2124 + "babel-helper-optimise-call-expression": "^6.24.1",
2125 + "babel-helper-replace-supers": "^6.24.1",
2126 + "babel-messages": "^6.23.0",
2127 + "babel-runtime": "^6.22.0",
2128 + "babel-template": "^6.24.1",
2129 + "babel-traverse": "^6.24.1",
2130 + "babel-types": "^6.24.1"
2131 + }
2132 + },
2133 + "node_modules/babel-plugin-transform-es2015-computed-properties": {
2134 + "version": "6.24.1",
2135 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
2136 + "integrity": "sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==",
2137 + "dev": true,
2138 + "license": "MIT",
2139 + "dependencies": {
2140 + "babel-runtime": "^6.22.0",
2141 + "babel-template": "^6.24.1"
2142 + }
2143 + },
2144 + "node_modules/babel-plugin-transform-es2015-destructuring": {
2145 + "version": "6.23.0",
2146 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
2147 + "integrity": "sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==",
2148 + "dev": true,
2149 + "license": "MIT",
2150 + "dependencies": {
2151 + "babel-runtime": "^6.22.0"
2152 + }
2153 + },
2154 + "node_modules/babel-plugin-transform-es2015-duplicate-keys": {
2155 + "version": "6.24.1",
2156 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
2157 + "integrity": "sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==",
2158 + "dev": true,
2159 + "license": "MIT",
2160 + "dependencies": {
2161 + "babel-runtime": "^6.22.0",
2162 + "babel-types": "^6.24.1"
2163 + }
2164 + },
2165 + "node_modules/babel-plugin-transform-es2015-for-of": {
2166 + "version": "6.23.0",
2167 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
2168 + "integrity": "sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==",
2169 + "dev": true,
2170 + "license": "MIT",
2171 + "dependencies": {
2172 + "babel-runtime": "^6.22.0"
2173 + }
2174 + },
2175 + "node_modules/babel-plugin-transform-es2015-function-name": {
2176 + "version": "6.24.1",
2177 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
2178 + "integrity": "sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==",
2179 + "dev": true,
2180 + "license": "MIT",
2181 + "dependencies": {
2182 + "babel-helper-function-name": "^6.24.1",
2183 + "babel-runtime": "^6.22.0",
2184 + "babel-types": "^6.24.1"
2185 + }
2186 + },
2187 + "node_modules/babel-plugin-transform-es2015-literals": {
2188 + "version": "6.22.0",
2189 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
2190 + "integrity": "sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==",
2191 + "dev": true,
2192 + "license": "MIT",
2193 + "dependencies": {
2194 + "babel-runtime": "^6.22.0"
2195 + }
2196 + },
2197 + "node_modules/babel-plugin-transform-es2015-modules-amd": {
2198 + "version": "6.24.1",
2199 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
2200 + "integrity": "sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==",
2201 + "dev": true,
2202 + "license": "MIT",
2203 + "dependencies": {
2204 + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
2205 + "babel-runtime": "^6.22.0",
2206 + "babel-template": "^6.24.1"
2207 + }
2208 + },
2209 + "node_modules/babel-plugin-transform-es2015-modules-commonjs": {
2210 + "version": "6.26.2",
2211 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
2212 + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
2213 + "dev": true,
2214 + "license": "MIT",
2215 + "dependencies": {
2216 + "babel-plugin-transform-strict-mode": "^6.24.1",
2217 + "babel-runtime": "^6.26.0",
2218 + "babel-template": "^6.26.0",
2219 + "babel-types": "^6.26.0"
2220 + }
2221 + },
2222 + "node_modules/babel-plugin-transform-es2015-modules-systemjs": {
2223 + "version": "6.24.1",
2224 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
2225 + "integrity": "sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==",
2226 + "dev": true,
2227 + "license": "MIT",
2228 + "dependencies": {
2229 + "babel-helper-hoist-variables": "^6.24.1",
2230 + "babel-runtime": "^6.22.0",
2231 + "babel-template": "^6.24.1"
2232 + }
2233 + },
2234 + "node_modules/babel-plugin-transform-es2015-modules-umd": {
2235 + "version": "6.24.1",
2236 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
2237 + "integrity": "sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==",
2238 + "dev": true,
2239 + "license": "MIT",
2240 + "dependencies": {
2241 + "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
2242 + "babel-runtime": "^6.22.0",
2243 + "babel-template": "^6.24.1"
2244 + }
2245 + },
2246 + "node_modules/babel-plugin-transform-es2015-object-super": {
2247 + "version": "6.24.1",
2248 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
2249 + "integrity": "sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==",
2250 + "dev": true,
2251 + "license": "MIT",
2252 + "dependencies": {
2253 + "babel-helper-replace-supers": "^6.24.1",
2254 + "babel-runtime": "^6.22.0"
2255 + }
2256 + },
2257 + "node_modules/babel-plugin-transform-es2015-parameters": {
2258 + "version": "6.24.1",
2259 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
2260 + "integrity": "sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==",
2261 + "dev": true,
2262 + "license": "MIT",
2263 + "dependencies": {
2264 + "babel-helper-call-delegate": "^6.24.1",
2265 + "babel-helper-get-function-arity": "^6.24.1",
2266 + "babel-runtime": "^6.22.0",
2267 + "babel-template": "^6.24.1",
2268 + "babel-traverse": "^6.24.1",
2269 + "babel-types": "^6.24.1"
2270 + }
2271 + },
2272 + "node_modules/babel-plugin-transform-es2015-shorthand-properties": {
2273 + "version": "6.24.1",
2274 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
2275 + "integrity": "sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==",
2276 + "dev": true,
2277 + "license": "MIT",
2278 + "dependencies": {
2279 + "babel-runtime": "^6.22.0",
2280 + "babel-types": "^6.24.1"
2281 + }
2282 + },
2283 + "node_modules/babel-plugin-transform-es2015-spread": {
2284 + "version": "6.22.0",
2285 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
2286 + "integrity": "sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==",
2287 + "dev": true,
2288 + "license": "MIT",
2289 + "dependencies": {
2290 + "babel-runtime": "^6.22.0"
2291 + }
2292 + },
2293 + "node_modules/babel-plugin-transform-es2015-sticky-regex": {
2294 + "version": "6.24.1",
2295 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
2296 + "integrity": "sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==",
2297 + "dev": true,
2298 + "license": "MIT",
2299 + "dependencies": {
2300 + "babel-helper-regex": "^6.24.1",
2301 + "babel-runtime": "^6.22.0",
2302 + "babel-types": "^6.24.1"
2303 + }
2304 + },
2305 + "node_modules/babel-plugin-transform-es2015-template-literals": {
2306 + "version": "6.22.0",
2307 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
2308 + "integrity": "sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==",
2309 + "dev": true,
2310 + "license": "MIT",
2311 + "dependencies": {
2312 + "babel-runtime": "^6.22.0"
2313 + }
2314 + },
2315 + "node_modules/babel-plugin-transform-es2015-typeof-symbol": {
2316 + "version": "6.23.0",
2317 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
2318 + "integrity": "sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==",
2319 + "dev": true,
2320 + "license": "MIT",
2321 + "dependencies": {
2322 + "babel-runtime": "^6.22.0"
2323 + }
2324 + },
2325 + "node_modules/babel-plugin-transform-es2015-unicode-regex": {
2326 + "version": "6.24.1",
2327 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
2328 + "integrity": "sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==",
2329 + "dev": true,
2330 + "license": "MIT",
2331 + "dependencies": {
2332 + "babel-helper-regex": "^6.24.1",
2333 + "babel-runtime": "^6.22.0",
2334 + "regexpu-core": "^2.0.0"
2335 + }
2336 + },
2337 + "node_modules/babel-plugin-transform-exponentiation-operator": {
2338 + "version": "6.24.1",
2339 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
2340 + "integrity": "sha512-LzXDmbMkklvNhprr20//RStKVcT8Cu+SQtX18eMHLhjHf2yFzwtQ0S2f0jQ+89rokoNdmwoSqYzAhq86FxlLSQ==",
2341 + "dev": true,
2342 + "license": "MIT",
2343 + "dependencies": {
2344 + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1",
2345 + "babel-plugin-syntax-exponentiation-operator": "^6.8.0",
2346 + "babel-runtime": "^6.22.0"
2347 + }
2348 + },
2349 + "node_modules/babel-plugin-transform-export-extensions": {
2350 + "version": "6.22.0",
2351 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz",
2352 + "integrity": "sha512-mtzELzINaYqdVglyZrDDVwkcFRuE7s6QUFWXxwffKAHB/NkfbJ2NJSytugB43ytIC8UVt30Ereyx+7gNyTkDLg==",
2353 + "dev": true,
2354 + "license": "MIT",
2355 + "dependencies": {
2356 + "babel-plugin-syntax-export-extensions": "^6.8.0",
2357 + "babel-runtime": "^6.22.0"
2358 + }
2359 + },
2360 + "node_modules/babel-plugin-transform-function-bind": {
2361 + "version": "6.22.0",
2362 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz",
2363 + "integrity": "sha512-9Ec4KYf1GurT39mlUjDSlN7HWSlB3u3mWRMogQbb+Y88lO0ZM3rJ0ADhPnQwWK9TbO6e/4E+Et1rrfGY9mFimA==",
2364 + "dev": true,
2365 + "license": "MIT",
2366 + "dependencies": {
2367 + "babel-plugin-syntax-function-bind": "^6.8.0",
2368 + "babel-runtime": "^6.22.0"
2369 + }
2370 + },
2371 + "node_modules/babel-plugin-transform-object-rest-spread": {
2372 + "version": "6.26.0",
2373 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz",
2374 + "integrity": "sha512-ocgA9VJvyxwt+qJB0ncxV8kb/CjfTcECUY4tQ5VT7nP6Aohzobm8CDFaQ5FHdvZQzLmf0sgDxB8iRXZXxwZcyA==",
2375 + "dev": true,
2376 + "license": "MIT",
2377 + "dependencies": {
2378 + "babel-plugin-syntax-object-rest-spread": "^6.8.0",
2379 + "babel-runtime": "^6.26.0"
2380 + }
2381 + },
2382 + "node_modules/babel-plugin-transform-regenerator": {
2383 + "version": "6.26.0",
2384 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
2385 + "integrity": "sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==",
2386 + "dev": true,
2387 + "license": "MIT",
2388 + "dependencies": {
2389 + "regenerator-transform": "^0.10.0"
2390 + }
2391 + },
2392 + "node_modules/babel-plugin-transform-strict-mode": {
2393 + "version": "6.24.1",
2394 + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
2395 + "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==",
2396 + "dev": true,
2397 + "license": "MIT",
2398 + "dependencies": {
2399 + "babel-runtime": "^6.22.0",
2400 + "babel-types": "^6.24.1"
2401 + }
2402 + },
2403 + "node_modules/babel-preset-es2015": {
2404 + "version": "6.24.1",
2405 + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
2406 + "integrity": "sha512-XfwUqG1Ry6R43m4Wfob+vHbIVBIqTg/TJY4Snku1iIzeH7mUnwHA8Vagmv+ZQbPwhS8HgsdQvy28Py3k5zpoFQ==",
2407 + "deprecated": "🙌 Thanks for using Babel: we recommend using babel-preset-env now: please read https://babeljs.io/env to update!",
2408 + "dev": true,
2409 + "license": "MIT",
2410 + "dependencies": {
2411 + "babel-plugin-check-es2015-constants": "^6.22.0",
2412 + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
2413 + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
2414 + "babel-plugin-transform-es2015-block-scoping": "^6.24.1",
2415 + "babel-plugin-transform-es2015-classes": "^6.24.1",
2416 + "babel-plugin-transform-es2015-computed-properties": "^6.24.1",
2417 + "babel-plugin-transform-es2015-destructuring": "^6.22.0",
2418 + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1",
2419 + "babel-plugin-transform-es2015-for-of": "^6.22.0",
2420 + "babel-plugin-transform-es2015-function-name": "^6.24.1",
2421 + "babel-plugin-transform-es2015-literals": "^6.22.0",
2422 + "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
2423 + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
2424 + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
2425 + "babel-plugin-transform-es2015-modules-umd": "^6.24.1",
2426 + "babel-plugin-transform-es2015-object-super": "^6.24.1",
2427 + "babel-plugin-transform-es2015-parameters": "^6.24.1",
2428 + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
2429 + "babel-plugin-transform-es2015-spread": "^6.22.0",
2430 + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1",
2431 + "babel-plugin-transform-es2015-template-literals": "^6.22.0",
2432 + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0",
2433 + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
2434 + "babel-plugin-transform-regenerator": "^6.24.1"
2435 + }
2436 + },
2437 + "node_modules/babel-preset-stage-0": {
2438 + "version": "6.24.1",
2439 + "resolved": "https://registry.npmjs.org/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz",
2440 + "integrity": "sha512-MJD+xBbpsApbKlzAX0sOBF+VeFaUmv5s8FSOO7SSZpes1QgphCjq/UIGRFWSmQ/0i5bqQjLGCTXGGXqcLQ9JDA==",
2441 + "dev": true,
2442 + "license": "MIT",
2443 + "dependencies": {
2444 + "babel-plugin-transform-do-expressions": "^6.22.0",
2445 + "babel-plugin-transform-function-bind": "^6.22.0",
2446 + "babel-preset-stage-1": "^6.24.1"
2447 + }
2448 + },
2449 + "node_modules/babel-preset-stage-1": {
2450 + "version": "6.24.1",
2451 + "resolved": "https://registry.npmjs.org/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz",
2452 + "integrity": "sha512-rn+UOcd7BHDniq1SVxv2/AVVSVI1NK+hfS0I/iR6m6KbOi/aeBRcqBilqO73pd9VUpRXF2HFtlDuC9F2BEQqmg==",
2453 + "dev": true,
2454 + "license": "MIT",
2455 + "dependencies": {
2456 + "babel-plugin-transform-class-constructor-call": "^6.24.1",
2457 + "babel-plugin-transform-export-extensions": "^6.22.0",
2458 + "babel-preset-stage-2": "^6.24.1"
2459 + }
2460 + },
2461 + "node_modules/babel-preset-stage-2": {
2462 + "version": "6.24.1",
2463 + "resolved": "https://registry.npmjs.org/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz",
2464 + "integrity": "sha512-9F+nquz+37PrlTSBdpeQBKnQfAMNBnryXw+m4qBh35FNbJPfzZz+sjN2G5Uf1CRedU9PH7fJkTbYijxmkLX8Og==",
2465 + "dev": true,
2466 + "license": "MIT",
2467 + "dependencies": {
2468 + "babel-plugin-syntax-dynamic-import": "^6.18.0",
2469 + "babel-plugin-transform-class-properties": "^6.24.1",
2470 + "babel-plugin-transform-decorators": "^6.24.1",
2471 + "babel-preset-stage-3": "^6.24.1"
2472 + }
2473 + },
2474 + "node_modules/babel-preset-stage-3": {
2475 + "version": "6.24.1",
2476 + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz",
2477 + "integrity": "sha512-eCbEOF8uN0KypFXJmZXn2sTk7bPV9uM5xov7G/7BM08TbQEObsVs0cEWfy6NQySlfk7JBi/t+XJP1JkruYfthA==",
2478 + "dev": true,
2479 + "license": "MIT",
2480 + "dependencies": {
2481 + "babel-plugin-syntax-trailing-function-commas": "^6.22.0",
2482 + "babel-plugin-transform-async-generator-functions": "^6.24.1",
2483 + "babel-plugin-transform-async-to-generator": "^6.24.1",
2484 + "babel-plugin-transform-exponentiation-operator": "^6.24.1",
2485 + "babel-plugin-transform-object-rest-spread": "^6.22.0"
2486 + }
2487 + },
2488 + "node_modules/babel-register": {
2489 + "version": "6.26.0",
2490 + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
2491 + "integrity": "sha512-veliHlHX06wjaeY8xNITbveXSiI+ASFnOqvne/LaIJIqOWi2Ogmj91KOugEz/hoh/fwMhXNBJPCv8Xaz5CyM4A==",
2492 + "dev": true,
2493 + "license": "MIT",
2494 + "dependencies": {
2495 + "babel-core": "^6.26.0",
2496 + "babel-runtime": "^6.26.0",
2497 + "core-js": "^2.5.0",
2498 + "home-or-tmp": "^2.0.0",
2499 + "lodash": "^4.17.4",
2500 + "mkdirp": "^0.5.1",
2501 + "source-map-support": "^0.4.15"
2502 + }
2503 + },
2504 + "node_modules/babel-runtime": {
2505 + "version": "6.26.0",
2506 + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
2507 + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
2508 + "dev": true,
2509 + "license": "MIT",
2510 + "dependencies": {
2511 + "core-js": "^2.4.0",
2512 + "regenerator-runtime": "^0.11.0"
2513 + }
2514 + },
2515 + "node_modules/babel-template": {
2516 + "version": "6.26.0",
2517 + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
2518 + "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==",
2519 + "dev": true,
2520 + "license": "MIT",
2521 + "dependencies": {
2522 + "babel-runtime": "^6.26.0",
2523 + "babel-traverse": "^6.26.0",
2524 + "babel-types": "^6.26.0",
2525 + "babylon": "^6.18.0",
2526 + "lodash": "^4.17.4"
2527 + }
2528 + },
2529 + "node_modules/babel-traverse": {
2530 + "version": "6.26.0",
2531 + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
2532 + "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==",
2533 + "dev": true,
2534 + "license": "MIT",
2535 + "dependencies": {
2536 + "babel-code-frame": "^6.26.0",
2537 + "babel-messages": "^6.23.0",
2538 + "babel-runtime": "^6.26.0",
2539 + "babel-types": "^6.26.0",
2540 + "babylon": "^6.18.0",
2541 + "debug": "^2.6.8",
2542 + "globals": "^9.18.0",
2543 + "invariant": "^2.2.2",
2544 + "lodash": "^4.17.4"
2545 + }
2546 + },
2547 + "node_modules/babel-traverse/node_modules/debug": {
2548 + "version": "2.6.9",
2549 + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
2550 + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
2551 + "dev": true,
2552 + "license": "MIT",
2553 + "dependencies": {
2554 + "ms": "2.0.0"
2555 + }
2556 + },
2557 + "node_modules/babel-traverse/node_modules/ms": {
2558 + "version": "2.0.0",
2559 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
2560 + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
2561 + "dev": true,
2562 + "license": "MIT"
2563 + },
2564 + "node_modules/babel-types": {
2565 + "version": "6.26.0",
2566 + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
2567 + "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
2568 + "dev": true,
2569 + "license": "MIT",
2570 + "dependencies": {
2571 + "babel-runtime": "^6.26.0",
2572 + "esutils": "^2.0.2",
2573 + "lodash": "^4.17.4",
2574 + "to-fast-properties": "^1.0.3"
2575 + }
2576 + },
2577 + "node_modules/babylon": {
2578 + "version": "6.18.0",
2579 + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
2580 + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
2581 + "dev": true,
2582 + "license": "MIT",
2583 + "bin": {
2584 + "babylon": "bin/babylon.js"
2585 + }
2586 + },
1549 2587 "node_modules/balanced-match": {
1550 2588 "version": "1.0.2",
1551 2589 "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -2108,6 +3146,13 @@ "funding": {
2108 3146 "url": "https://github.com/sponsors/sindresorhus"
2109 3147 }
2110 3148 },
3149 + "node_modules/concat-map": {
3150 + "version": "0.0.1",
3151 + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
3152 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
3153 + "dev": true,
3154 + "license": "MIT"
3155 + },
2111 3156 "node_modules/concordance": {
2112 3157 "version": "5.0.4",
2113 3158 "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz",
@@ -2167,6 +3212,13 @@ "engines": {
2167 3212 "node": ">= 0.6"
2168 3213 }
2169 3214 },
3215 + "node_modules/convert-source-map": {
3216 + "version": "1.9.0",
3217 + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
3218 + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
3219 + "dev": true,
3220 + "license": "MIT"
3221 + },
2170 3222 "node_modules/convert-to-spaces": {
2171 3223 "version": "2.0.1",
2172 3224 "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz",
@@ -2202,6 +3254,15 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
2202 3254 "dev": true,
2203 3255 "license": "MIT"
2204 3256 },
3257 + "node_modules/core-js": {
3258 + "version": "2.6.12",
3259 + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
3260 + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
3261 + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
3262 + "dev": true,
3263 + "hasInstallScript": true,
3264 + "license": "MIT"
3265 + },
2205 3266 "node_modules/cors": {
2206 3267 "version": "2.8.6",
2207 3268 "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
@@ -2241,6 +3302,89 @@ "engines": {
2241 3302 "node": ">= 8"
2242 3303 }
2243 3304 },
3305 + "node_modules/cross-var": {
3306 + "version": "1.1.0",
3307 + "resolved": "https://registry.npmjs.org/cross-var/-/cross-var-1.1.0.tgz",
3308 + "integrity": "sha512-wIcFax9RNm5ayuORUeJ5MLxPbfh8XdZhhUpKutIszU46Fs9UIhEdPJ7+YguM+7FxEj+68hSQVyathVsIu84SiA==",
3309 + "dev": true,
3310 + "license": "MIT",
3311 + "dependencies": {
3312 + "babel-preset-es2015": "^6.18.0",
3313 + "babel-preset-stage-0": "^6.16.0",
3314 + "babel-register": "^6.18.0",
3315 + "cross-spawn": "^5.0.1",
3316 + "exit": "^0.1.2"
3317 + },
3318 + "bin": {
3319 + "cross-var": "index.js"
3320 + }
3321 + },
3322 + "node_modules/cross-var/node_modules/cross-spawn": {
3323 + "version": "5.1.0",
3324 + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
3325 + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==",
3326 + "dev": true,
3327 + "license": "MIT",
3328 + "dependencies": {
3329 + "lru-cache": "^4.0.1",
3330 + "shebang-command": "^1.2.0",
3331 + "which": "^1.2.9"
3332 + }
3333 + },
3334 + "node_modules/cross-var/node_modules/lru-cache": {
3335 + "version": "4.1.5",
3336 + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
3337 + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
3338 + "dev": true,
3339 + "license": "ISC",
3340 + "dependencies": {
3341 + "pseudomap": "^1.0.2",
3342 + "yallist": "^2.1.2"
3343 + }
3344 + },
3345 + "node_modules/cross-var/node_modules/shebang-command": {
3346 + "version": "1.2.0",
3347 + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
3348 + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==",
3349 + "dev": true,
3350 + "license": "MIT",
3351 + "dependencies": {
3352 + "shebang-regex": "^1.0.0"
3353 + },
3354 + "engines": {
3355 + "node": ">=0.10.0"
3356 + }
3357 + },
3358 + "node_modules/cross-var/node_modules/shebang-regex": {
3359 + "version": "1.0.0",
3360 + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
3361 + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==",
3362 + "dev": true,
3363 + "license": "MIT",
3364 + "engines": {
3365 + "node": ">=0.10.0"
3366 + }
3367 + },
3368 + "node_modules/cross-var/node_modules/which": {
3369 + "version": "1.3.1",
3370 + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
3371 + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
3372 + "dev": true,
3373 + "license": "ISC",
3374 + "dependencies": {
3375 + "isexe": "^2.0.0"
3376 + },
3377 + "bin": {
3378 + "which": "bin/which"
3379 + }
3380 + },
3381 + "node_modules/cross-var/node_modules/yallist": {
3382 + "version": "2.1.2",
3383 + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
3384 + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
3385 + "dev": true,
3386 + "license": "ISC"
3387 + },
2244 3388 "node_modules/csstype": {
2245 3389 "version": "3.2.3",
2246 3390 "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -2379,6 +3523,19 @@ "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==",
2379 3523 "devOptional": true,
2380 3524 "license": "MIT"
2381 3525 },
3526 + "node_modules/detect-indent": {
3527 + "version": "4.0.0",
3528 + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
3529 + "integrity": "sha512-BDKtmHlOzwI7iRuEkhzsnPoi5ypEhWAJB5RvHWe1kMr06js3uK5B3734i3ui5Yd+wOJV1cpE4JnivPD283GU/A==",
3530 + "dev": true,
3531 + "license": "MIT",
3532 + "dependencies": {
3533 + "repeating": "^2.0.0"
3534 + },
3535 + "engines": {
3536 + "node": ">=0.10.0"
3537 + }
3538 + },
2382 3539 "node_modules/detect-libc": {
2383 3540 "version": "2.1.2",
2384 3541 "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
@@ -2445,6 +3602,51 @@ "funding": {
2445 3602 "url": "https://dotenvx.com"
2446 3603 }
2447 3604 },
3605 + "node_modules/dotenv-cli": {
3606 + "version": "11.0.0",
3607 + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-11.0.0.tgz",
3608 + "integrity": "sha512-r5pA8idbk7GFWuHEU7trSTflWcdBpQEK+Aw17UrSHjS6CReuhrrPcyC3zcQBPQvhArRHnBo/h6eLH1fkCvNlww==",
3609 + "dev": true,
3610 + "license": "MIT",
3611 + "dependencies": {
3612 + "cross-spawn": "^7.0.6",
3613 + "dotenv": "^17.1.0",
3614 + "dotenv-expand": "^12.0.0",
3615 + "minimist": "^1.2.6"
3616 + },
3617 + "bin": {
3618 + "dotenv": "cli.js"
3619 + }
3620 + },
3621 + "node_modules/dotenv-cli/node_modules/dotenv": {
3622 + "version": "17.4.1",
3623 + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz",
3624 + "integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==",
3625 + "dev": true,
3626 + "license": "BSD-2-Clause",
3627 + "engines": {
3628 + "node": ">=12"
3629 + },
3630 + "funding": {
3631 + "url": "https://dotenvx.com"
3632 + }
3633 + },
3634 + "node_modules/dotenv-expand": {
3635 + "version": "12.0.3",
3636 + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz",
3637 + "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==",
3638 + "dev": true,
3639 + "license": "BSD-2-Clause",
3640 + "dependencies": {
3641 + "dotenv": "^16.4.5"
3642 + },
3643 + "engines": {
3644 + "node": ">=12"
3645 + },
3646 + "funding": {
3647 + "url": "https://dotenvx.com"
3648 + }
3649 + },
2448 3650 "node_modules/dunder-proto": {
2449 3651 "version": "1.0.1",
2450 3652 "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2735,6 +3937,15 @@ "node": ">=18"
2735 3937 },
2736 3938 "funding": {
2737 3939 "url": "https://github.com/sponsors/sindresorhus"
3940 + }
3941 + },
3942 + "node_modules/exit": {
3943 + "version": "0.1.2",
3944 + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
3945 + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
3946 + "dev": true,
3947 + "engines": {
3948 + "node": ">= 0.8.0"
2738 3949 }
2739 3950 },
2740 3951 "node_modules/expand-template": {
@@ -3217,6 +4428,16 @@ "engines": {
3217 4428 "node": ">= 6"
3218 4429 }
3219 4430 },
4431 + "node_modules/globals": {
4432 + "version": "9.18.0",
4433 + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
4434 + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
4435 + "dev": true,
4436 + "license": "MIT",
4437 + "engines": {
4438 + "node": ">=0.10.0"
4439 + }
4440 + },
3220 4441 "node_modules/globby": {
3221 4442 "version": "14.1.0",
3222 4443 "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz",
@@ -3271,6 +4492,29 @@ "integrity": "sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==",
3271 4492 "devOptional": true,
3272 4493 "license": "MIT"
3273 4494 },
4495 + "node_modules/has-ansi": {
4496 + "version": "2.0.0",
4497 + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
4498 + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
4499 + "dev": true,
4500 + "license": "MIT",
4501 + "dependencies": {
4502 + "ansi-regex": "^2.0.0"
4503 + },
4504 + "engines": {
4505 + "node": ">=0.10.0"
4506 + }
4507 + },
4508 + "node_modules/has-ansi/node_modules/ansi-regex": {
4509 + "version": "2.1.1",
4510 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
4511 + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
4512 + "dev": true,
4513 + "license": "MIT",
4514 + "engines": {
4515 + "node": ">=0.10.0"
4516 + }
4517 + },
3274 4518 "node_modules/has-symbols": {
3275 4519 "version": "1.1.0",
3276 4520 "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -3309,6 +4553,20 @@ "function-bind": "^1.1.2"
3309 4553 },
3310 4554 "engines": {
3311 4555 "node": ">= 0.4"
4556 + }
4557 + },
4558 + "node_modules/home-or-tmp": {
4559 + "version": "2.0.0",
4560 + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
4561 + "integrity": "sha512-ycURW7oUxE2sNiPVw1HVEFsW+ecOpJ5zaj7eC0RlwhibhRBod20muUN8qu/gzx956YrLolVvs1MTXwKgC2rVEg==",
4562 + "dev": true,
4563 + "license": "MIT",
4564 + "dependencies": {
4565 + "os-homedir": "^1.0.0",
4566 + "os-tmpdir": "^1.0.1"
4567 + },
4568 + "engines": {
4569 + "node": ">=0.10.0"
3312 4570 }
3313 4571 },
3314 4572 "node_modules/hono": {
@@ -3463,6 +4721,16 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
3463 4721 "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
3464 4722 "license": "ISC"
3465 4723 },
4724 + "node_modules/invariant": {
4725 + "version": "2.2.4",
4726 + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
4727 + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
4728 + "dev": true,
4729 + "license": "MIT",
4730 + "dependencies": {
4731 + "loose-envify": "^1.0.0"
4732 + }
4733 + },
3466 4734 "node_modules/ipaddr.js": {
3467 4735 "version": "1.9.1",
3468 4736 "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -3503,6 +4771,19 @@ "dev": true,
3503 4771 "license": "MIT",
3504 4772 "engines": {
3505 4773 "node": ">=0.10.0"
4774 + }
4775 + },
4776 + "node_modules/is-finite": {
4777 + "version": "1.1.0",
4778 + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
4779 + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
4780 + "dev": true,
4781 + "license": "MIT",
4782 + "engines": {
4783 + "node": ">=0.10.0"
4784 + },
4785 + "funding": {
4786 + "url": "https://github.com/sponsors/sindresorhus"
3506 4787 }
3507 4788 },
3508 4789 "node_modules/is-fullwidth-code-point": {
@@ -3646,6 +4927,13 @@ "engines": {
3646 4927 "node": ">= 0.8"
3647 4928 }
3648 4929 },
4930 + "node_modules/js-tokens": {
4931 + "version": "3.0.2",
4932 + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
4933 + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
4934 + "dev": true,
4935 + "license": "MIT"
4936 + },
3649 4937 "node_modules/js-yaml": {
3650 4938 "version": "3.14.2",
3651 4939 "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
@@ -3660,6 +4948,16 @@ "bin": {
3660 4948 "js-yaml": "bin/js-yaml.js"
3661 4949 }
3662 4950 },
4951 + "node_modules/jsesc": {
4952 + "version": "1.3.0",
4953 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
4954 + "integrity": "sha512-Mke0DA0QjUWuJlhsE0ZPPhYiJkRap642SmI/4ztCFaUs6V2AiH1sfecc+57NgaryfAA2VR3v6O+CSjC1jZJKOA==",
4955 + "dev": true,
4956 + "license": "MIT",
4957 + "bin": {
4958 + "jsesc": "bin/jsesc"
4959 + }
4960 + },
3663 4961 "node_modules/json5": {
3664 4962 "version": "2.2.3",
3665 4963 "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -3794,6 +5092,19 @@ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
3794 5092 "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
3795 5093 "devOptional": true,
3796 5094 "license": "Apache-2.0"
5095 + },
5096 + "node_modules/loose-envify": {
5097 + "version": "1.4.0",
5098 + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
5099 + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
5100 + "dev": true,
5101 + "license": "MIT",
5102 + "dependencies": {
5103 + "js-tokens": "^3.0.0 || ^4.0.0"
5104 + },
5105 + "bin": {
5106 + "loose-envify": "cli.js"
5107 + }
3797 5108 },
3798 5109 "node_modules/lru-cache": {
3799 5110 "version": "10.4.3",
@@ -4058,6 +5369,19 @@ "engines": {
4058 5369 "node": ">= 18"
4059 5370 }
4060 5371 },
5372 + "node_modules/mkdirp": {
5373 + "version": "0.5.6",
5374 + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
5375 + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
5376 + "dev": true,
5377 + "license": "MIT",
5378 + "dependencies": {
5379 + "minimist": "^1.2.6"
5380 + },
5381 + "bin": {
5382 + "mkdirp": "bin/cmd.js"
5383 + }
5384 + },
4061 5385 "node_modules/mkdirp-classic": {
4062 5386 "version": "0.5.3",
4063 5387 "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
@@ -4377,6 +5701,26 @@ "dependencies": {
4377 5701 "wrappy": "1"
4378 5702 }
4379 5703 },
5704 + "node_modules/os-homedir": {
5705 + "version": "1.0.2",
5706 + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
5707 + "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==",
5708 + "dev": true,
5709 + "license": "MIT",
5710 + "engines": {
5711 + "node": ">=0.10.0"
5712 + }
5713 + },
5714 + "node_modules/os-tmpdir": {
5715 + "version": "1.0.2",
5716 + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
5717 + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
5718 + "dev": true,
5719 + "license": "MIT",
5720 + "engines": {
5721 + "node": ">=0.10.0"
5722 + }
5723 + },
4380 5724 "node_modules/p-map": {
4381 5725 "version": "7.0.4",
4382 5726 "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz",
@@ -4434,6 +5778,16 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
4434 5778 "license": "MIT",
4435 5779 "engines": {
4436 5780 "node": ">= 0.8"
5781 + }
5782 + },
5783 + "node_modules/path-is-absolute": {
5784 + "version": "1.0.1",
5785 + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
5786 + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
5787 + "dev": true,
5788 + "license": "MIT",
5789 + "engines": {
5790 + "node": ">=0.10.0"
4437 5791 }
4438 5792 },
4439 5793 "node_modules/path-key": {
@@ -4781,6 +6135,16 @@ "optional": true
4781 6135 }
4782 6136 }
4783 6137 },
6138 + "node_modules/private": {
6139 + "version": "0.1.8",
6140 + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
6141 + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
6142 + "dev": true,
6143 + "license": "MIT",
6144 + "engines": {
6145 + "node": ">= 0.6"
6146 + }
6147 + },
4784 6148 "node_modules/proper-lockfile": {
4785 6149 "version": "4.1.2",
4786 6150 "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
@@ -4812,6 +6176,13 @@ },
4812 6176 "engines": {
4813 6177 "node": ">= 0.10"
4814 6178 }
6179 + },
6180 + "node_modules/pseudomap": {
6181 + "version": "1.0.2",
6182 + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
6183 + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
6184 + "dev": true,
6185 + "license": "ISC"
4815 6186 },
4816 6187 "node_modules/pump": {
4817 6188 "version": "3.0.3",
@@ -4989,6 +6360,32 @@ "type": "individual",
4989 6360 "url": "https://paulmillr.com/funding/"
4990 6361 }
4991 6362 },
6363 + "node_modules/regenerate": {
6364 + "version": "1.4.2",
6365 + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
6366 + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
6367 + "dev": true,
6368 + "license": "MIT"
6369 + },
6370 + "node_modules/regenerator-runtime": {
6371 + "version": "0.11.1",
6372 + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
6373 + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
6374 + "dev": true,
6375 + "license": "MIT"
6376 + },
6377 + "node_modules/regenerator-transform": {
6378 + "version": "0.10.1",
6379 + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
6380 + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
6381 + "dev": true,
6382 + "license": "BSD",
6383 + "dependencies": {
6384 + "babel-runtime": "^6.18.0",
6385 + "babel-types": "^6.19.0",
6386 + "private": "^0.1.6"
6387 + }
6388 + },
4992 6389 "node_modules/regexp-to-ast": {
4993 6390 "version": "0.5.0",
4994 6391 "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz",
@@ -4996,6 +6393,47 @@ "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==",
4996 6393 "devOptional": true,
4997 6394 "license": "MIT"
4998 6395 },
6396 + "node_modules/regexpu-core": {
6397 + "version": "2.0.0",
6398 + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
6399 + "integrity": "sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==",
6400 + "dev": true,
6401 + "license": "MIT",
6402 + "dependencies": {
6403 + "regenerate": "^1.2.1",
6404 + "regjsgen": "^0.2.0",
6405 + "regjsparser": "^0.1.4"
6406 + }
6407 + },
6408 + "node_modules/regjsgen": {
6409 + "version": "0.2.0",
6410 + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
6411 + "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==",
6412 + "dev": true,
6413 + "license": "MIT"
6414 + },
6415 + "node_modules/regjsparser": {
6416 + "version": "0.1.5",
6417 + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
6418 + "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==",
6419 + "dev": true,
6420 + "license": "BSD",
6421 + "dependencies": {
6422 + "jsesc": "~0.5.0"
6423 + },
6424 + "bin": {
6425 + "regjsparser": "bin/parser"
6426 + }
6427 + },
6428 + "node_modules/regjsparser/node_modules/jsesc": {
6429 + "version": "0.5.0",
6430 + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
6431 + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
6432 + "dev": true,
6433 + "bin": {
6434 + "jsesc": "bin/jsesc"
6435 + }
6436 + },
4999 6437 "node_modules/remeda": {
5000 6438 "version": "2.33.4",
5001 6439 "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.33.4.tgz",
@@ -5004,6 +6442,19 @@ "devOptional": true,
5004 6442 "license": "MIT",
5005 6443 "funding": {
5006 6444 "url": "https://github.com/sponsors/remeda"
6445 + }
6446 + },
6447 + "node_modules/repeating": {
6448 + "version": "2.0.1",
6449 + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
6450 + "integrity": "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==",
6451 + "dev": true,
6452 + "license": "MIT",
6453 + "dependencies": {
6454 + "is-finite": "^1.0.0"
6455 + },
6456 + "engines": {
6457 + "node": ">=0.10.0"
5007 6458 }
5008 6459 },
5009 6460 "node_modules/require-directory": {
@@ -5404,6 +6855,26 @@ "funding": {
5404 6855 "url": "https://github.com/chalk/slice-ansi?sponsor=1"
5405 6856 }
5406 6857 },
6858 + "node_modules/source-map": {
6859 + "version": "0.5.7",
6860 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
6861 + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
6862 + "dev": true,
6863 + "license": "BSD-3-Clause",
6864 + "engines": {
6865 + "node": ">=0.10.0"
6866 + }
6867 + },
6868 + "node_modules/source-map-support": {
6869 + "version": "0.4.18",
6870 + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
6871 + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
6872 + "dev": true,
6873 + "license": "MIT",
6874 + "dependencies": {
6875 + "source-map": "^0.5.6"
6876 + }
6877 + },
5407 6878 "node_modules/split2": {
5408 6879 "version": "4.2.0",
5409 6880 "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -5675,6 +7146,16 @@ "engines": {
5675 7146 "node": ">=14.18.0"
5676 7147 }
5677 7148 },
7149 + "node_modules/supports-color": {
7150 + "version": "2.0.0",
7151 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
7152 + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
7153 + "dev": true,
7154 + "license": "MIT",
7155 + "engines": {
7156 + "node": ">=0.8.0"
7157 + }
7158 + },
5678 7159 "node_modules/tar": {
5679 7160 "version": "7.5.2",
5680 7161 "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz",
@@ -5760,6 +7241,16 @@ "engines": {
5760 7241 "node": ">=18"
5761 7242 }
5762 7243 },
7244 + "node_modules/to-fast-properties": {
7245 + "version": "1.0.3",
7246 + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
7247 + "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==",
7248 + "dev": true,
7249 + "license": "MIT",
7250 + "engines": {
7251 + "node": ">=0.10.0"
7252 + }
7253 + },
5763 7254 "node_modules/to-regex-range": {
5764 7255 "version": "5.0.1",
5765 7256 "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -5788,6 +7279,16 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
5788 7279 "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
5789 7280 "dev": true,
5790 7281 "license": "MIT"
7282 + },
7283 + "node_modules/trim-right": {
7284 + "version": "1.0.1",
7285 + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
7286 + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==",
7287 + "dev": true,
7288 + "license": "MIT",
7289 + "engines": {
7290 + "node": ">=0.10.0"
7291 + }
5791 7292 },
5792 7293 "node_modules/ts-node": {
5793 7294 "version": "10.9.2",
api/package.json
@@ -11,10 +11,10 @@ },
11 11 "scripts": {
12 12 "build": "tsc && tsc-alias",
13 13 "start": "ts-node -r tsconfig-paths/register src/index.ts",
14 - "test": "npm run test:setup && NODE_ENV=test npx ava tests/database.test.ts",
14 + "test": "npm run test:setup && NODE_ENV=test npx ava",
15 15 "test:setup": "npm run test:setup:generate && npm run test:setup:migrate",
16 - "test:setup:generate": "DATABASE_URL='postgres://user:password@orbit_db:5432/orbit_test' npx prisma generate",
17 - "test:setup:migrate": "DATABASE_URL='postgres://user:password@orbit_db:5432/orbit_test' npx prisma migrate deploy",
16 + "test:setup:generate": "dotenv -e .env.test npx prisma generate",
17 + "test:setup:migrate": "dotenv -e .env.test npx prisma db push",
18 18 "test:watch": "npm run test:setup && NODE_ENV=test npx ava --watch",
19 19 "dev": "prisma generate && prisma migrate deploy && npm run start"
20 20 },
@@ -40,6 +40,8 @@ "@types/express": "^5.0.6",
40 40 "@types/morgan": "^1.9.10",
41 41 "@types/node": "^25.5.2",
42 42 "ava": "^6.4.1",
43 + "cross-var": "^1.1.0",
44 + "dotenv-cli": "^11.0.0",
43 45 "prisma": "^7.5.0",
44 46 "supertest": "^7.1.4",
45 47 "ts-node": "^10.9.2",
api/tests/database-auth.test.ts
@@ -0,0 +1,254 @@
1 + import test from "ava";
2 + import { PrismaClient } from '@prisma/client';
3 + import { PrismaPg } from '@prisma/adapter-pg';
4 + import bcrypt from 'bcryptjs';
5 + import 'dotenv/config';
6 +
7 + let prisma: PrismaClient;
8 +
9 + test.before(() => {
10 + process.env.NODE_ENV = 'test';
11 +
12 + const connectionString = process.env.DATABASE_URL || 'postgres://user:password@orbit_db:5432/orbit_test';
13 + const adapter = new PrismaPg({ connectionString });
14 + prisma = new PrismaClient({ adapter });
15 + });
16 +
17 + test.beforeEach(async () => {
18 + // Clean database before each test
19 + try {
20 + await prisma.user.deleteMany({});
21 + } catch (err) {
22 + console.error('Error cleaning test DB:', err);
23 + }
24 + });
25 +
26 + test.after.always(async () => {
27 + await prisma?.$disconnect();
28 + });
29 +
30 + // Basic database connectivity test
31 + test("database connection works", async (t) => {
32 + const result = await prisma.$queryRaw`SELECT 1 as test`;
33 + t.truthy(result);
34 + });
35 +
36 + // User creation and authentication tests
37 + test("can create user with encrypted password", async (t) => {
38 + const encryptedPassword = bcrypt.hashSync("testPassword123", 10);
39 +
40 + const user = await prisma.user.create({
41 + data: {
42 + email: "test@example.com",
43 + firstName: "Test",
44 + lastName: "User",
45 + encryptedPassword: encryptedPassword
46 + }
47 + });
48 +
49 + t.is(user.email, "test@example.com");
50 + t.is(user.firstName, "Test");
51 + t.truthy(user.id);
52 +
53 + // Verify password can be validated
54 + t.true(bcrypt.compareSync("testPassword123", user.encryptedPassword));
55 + t.false(bcrypt.compareSync("wrongPassword", user.encryptedPassword));
56 + });
57 +
58 + test("can find user by email for authentication", async (t) => {
59 + // Create user
60 + await prisma.user.create({
61 + data: {
62 + email: "login@example.com",
63 + firstName: "Login",
64 + lastName: "User",
65 + encryptedPassword: bcrypt.hashSync("password123", 10)
66 + }
67 + });
68 +
69 + // Simulate login lookup
70 + const foundUser = await prisma.user.findUnique({
71 + where: { email: "login@example.com" }
72 + });
73 +
74 + t.truthy(foundUser);
75 + t.is(foundUser!.email, "login@example.com");
76 + t.true(bcrypt.compareSync("password123", foundUser!.encryptedPassword));
77 + t.false(bcrypt.compareSync("wrongPassword", foundUser!.encryptedPassword));
78 + });
79 +
80 + test("email uniqueness constraint prevents duplicate registrations", async (t) => {
81 + const email = "duplicate@example.com";
82 +
83 + // Create first user
84 + await prisma.user.create({
85 + data: {
86 + email,
87 + firstName: "First",
88 + lastName: "User",
89 + encryptedPassword: bcrypt.hashSync("password", 10)
90 + }
91 + });
92 +
93 + // Try to create second user with same email - should fail
94 + await t.throwsAsync(
95 + async () => {
96 + await prisma.user.create({
97 + data: {
98 + email,
99 + firstName: "Second",
100 + lastName: "User",
101 + encryptedPassword: bcrypt.hashSync("password", 10)
102 + }
103 + });
104 + }
105 + );
106 + });
107 +
108 + test("password reset flow works with database", async (t) => {
109 + const resetToken = "reset-token-123";
110 + const resetExpires = new Date(Date.now() + 3600000); // 1 hour from now
111 +
112 + // Create user
113 + const user = await prisma.user.create({
114 + data: {
115 + email: "reset@example.com",
116 + firstName: "Reset",
117 + lastName: "User",
118 + encryptedPassword: bcrypt.hashSync("oldPassword", 10)
119 + }
120 + });
121 +
122 + // Step 1: Set reset token (forgot password)
123 + await prisma.user.update({
124 + where: { id: user.id },
125 + data: {
126 + passwordResetToken: resetToken,
127 + passwordResetExpires: resetExpires
128 + }
129 + });
130 +
131 + // Step 2: Find user by reset token
132 + const userWithToken = await prisma.user.findFirst({
133 + where: {
134 + passwordResetToken: resetToken,
135 + passwordResetExpires: { gte: new Date() } // Token not expired
136 + }
137 + });
138 +
139 + t.truthy(userWithToken);
140 + t.is(userWithToken!.email, "reset@example.com");
141 +
142 + // Step 3: Reset password
143 + const newPassword = bcrypt.hashSync("newPassword123", 10);
144 + const updatedUser = await prisma.user.update({
145 + where: { id: user.id },
146 + data: {
147 + encryptedPassword: newPassword,
148 + passwordResetToken: null,
149 + passwordResetExpires: null
150 + }
151 + });
152 +
153 + // Verify new password works and reset fields are cleared
154 + t.true(bcrypt.compareSync("newPassword123", updatedUser.encryptedPassword));
155 + t.false(bcrypt.compareSync("oldPassword", updatedUser.encryptedPassword));
156 + t.is(updatedUser.passwordResetToken, null);
157 + t.is(updatedUser.passwordResetExpires, null);
158 + });
159 +
160 + test("expired reset tokens are not found", async (t) => {
161 + const expiredResetToken = "expired-token";
162 + const pastDate = new Date(Date.now() - 3600000); // 1 hour ago
163 +
164 + // Create user with expired token
165 + const user = await prisma.user.create({
166 + data: {
167 + email: "expired@example.com",
168 + firstName: "Expired",
169 + lastName: "User",
170 + encryptedPassword: bcrypt.hashSync("password", 10),
171 + passwordResetToken: expiredResetToken,
172 + passwordResetExpires: pastDate
173 + }
174 + });
175 +
176 + // Try to find user by expired token
177 + const foundUser = await prisma.user.findFirst({
178 + where: {
179 + passwordResetToken: expiredResetToken,
180 + passwordResetExpires: { gte: new Date() } // Token not expired
181 + }
182 + });
183 +
184 + // Should not find the user because token is expired
185 + t.is(foundUser, null);
186 +
187 + // But user still exists
188 + const stillExists = await prisma.user.findUnique({ where: { id: user.id } });
189 + t.truthy(stillExists);
190 + });
191 +
192 + test("user profile update works", async (t) => {
193 + // Create user
194 + const user = await prisma.user.create({
195 + data: {
196 + email: "profile@example.com",
197 + firstName: "Original",
198 + lastName: "Name",
199 + encryptedPassword: bcrypt.hashSync("password", 10)
200 + }
201 + });
202 +
203 + // Update profile
204 + const updatedUser = await prisma.user.update({
205 + where: { id: user.id },
206 + data: {
207 + firstName: "Updated",
208 + lastName: "Profile"
209 + }
210 + });
211 +
212 + t.is(updatedUser.firstName, "Updated");
213 + t.is(updatedUser.lastName, "Profile");
214 + t.is(updatedUser.email, "profile@example.com"); // Should remain unchanged
215 + t.true(updatedUser.updatedAt > user.updatedAt); // updatedAt should be newer
216 + });
217 +
218 + test("timestamps are automatically managed", async (t) => {
219 + const beforeCreate = new Date();
220 +
221 + const user = await prisma.user.create({
222 + data: {
223 + email: "timestamps@example.com",
224 + firstName: "Time",
225 + lastName: "Stamps",
226 + encryptedPassword: bcrypt.hashSync("password", 10)
227 + }
228 + });
229 +
230 + const afterCreate = new Date();
231 +
232 + // Check creation timestamps
233 + t.true(user.createdAt >= beforeCreate);
234 + t.true(user.createdAt <= afterCreate);
235 + t.true(user.updatedAt >= beforeCreate);
236 + t.true(user.updatedAt <= afterCreate);
237 +
238 + // Wait and update to test updatedAt behavior
239 + await new Promise(resolve => setTimeout(resolve, 100));
240 + const beforeUpdate = new Date();
241 +
242 + const updatedUser = await prisma.user.update({
243 + where: { id: user.id },
244 + data: { firstName: "Updated" }
245 + });
246 +
247 + const afterUpdate = new Date();
248 +
249 + // createdAt should remain the same, updatedAt should be newer
250 + t.deepEqual(updatedUser.createdAt, user.createdAt);
251 + t.true(updatedUser.updatedAt >= beforeUpdate);
252 + t.true(updatedUser.updatedAt <= afterUpdate);
253 + t.true(updatedUser.updatedAt > user.updatedAt);
254 + });
@@ -1,98 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "./test-helper.js";
5 -
6 - let app;
7 - let db;
8 - let userId;
9 -
10 - test.before(() => {
11 - db = setupTestDb();
12 - app = createApp(db);
13 - });
14 -
15 - test.beforeEach(async () => {
16 - cleanupTestDb();
17 -
18 - // Create a user for the mood tests
19 - const res = await request(app)
20 - .post("/users")
21 - .send({
22 - email: "maria@example.com",
23 - password: "123456",
24 - first_name: "Maria",
25 - last_name: "Silva",
26 - });
27 -
28 - userId = res.body.id;
29 - });
30 -
31 - test.serial("Registrar humor", async (t) => {
32 - const res = await request(app).post("/mood").send({
33 - user_id: userId,
34 - stress_level: 3,
35 - anxiety_level: 4,
36 - energy_level: 5,
37 - title: "Bad day",
38 - description: "Stressado com a faculdade",
39 - recorded_at: new Date().toISOString()
40 - });
41 -
42 - t.is(res.status, 201);
43 - t.truthy(res.body.id);
44 - });
45 -
46 - test.serial("Listar humores do usuário", async (t) => {
47 - const res = await request(app).get(`/mood/user/${userId}`);
48 - t.is(res.status, 200);
49 - t.true(Array.isArray(res.body.moods));
50 - });
51 -
52 - test.serial("Falha ao registrar humor sem stress_level", async (t) => {
53 - const res = await request(app).post("/mood").send({
54 - user_id: userId,
55 - anxiety_level: 4,
56 - energy_level: 5,
57 - title: "Test",
58 - recorded_at: new Date().toISOString()
59 - });
60 -
61 - t.is(res.status, 400);
62 - t.is(res.body.error, "Campos faltantes");
63 - });
64 -
65 - test.serial("Listar múltiplos humores do usuário", async (t) => {
66 - // Ensure we have a valid userId
67 - t.truthy(userId, "userId should be defined");
68 -
69 - // Create first mood entry
70 - const res1 = await request(app).post("/mood").send({
71 - user_id: userId,
72 - stress_level: 3,
73 - anxiety_level: 4,
74 - energy_level: 6,
75 - title: "Morning",
76 - description: "Morning mood",
77 - recorded_at: new Date().toISOString()
78 - });
79 - t.is(res1.status, 201);
80 -
81 - // Create second mood entry
82 - const res2 = await request(app).post("/mood").send({
83 - user_id: userId,
84 - stress_level: 2,
85 - anxiety_level: 2,
86 - energy_level: 8,
87 - title: "Evening",
88 - description: "Evening mood",
89 - recorded_at: new Date().toISOString()
90 - });
91 - t.is(res2.status, 201);
92 -
93 - const res = await request(app).get(`/mood/user/${userId}`);
94 - t.is(res.status, 200);
95 - t.true(Array.isArray(res.body.moods));
96 - t.is(res.body.moods.length, 2);
97 - });
98 -
@@ -1,498 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
5 -
6 - let app;
7 - let db;
8 - let userId;
9 -
10 - test.before(() => {
11 - db = setupTestDb();
12 - app = createApp(db);
13 - });
14 -
15 - test.beforeEach(async () => {
16 - cleanupTestDb();
17 -
18 - // Create a test user with unique email
19 - const uniqueEmail = `mood_${Date.now()}_${Math.random()}@example.com`;
20 - const res = await request(app)
21 - .post("/users")
22 - .send({
23 - email: uniqueEmail,
24 - password: "senha123",
25 - first_name: "Mood",
26 - last_name: "Tester"
27 - });
28 -
29 - if (res.status !== 201) {
30 - throw new Error(`Failed to create test user: ${JSON.stringify(res.body)}`);
31 - }
32 -
33 - userId = res.body.id;
34 - });
35 -
36 - test("Criar registro de humor com todos os campos obrigatórios", async (t) => {
37 - const now = new Date().toISOString();
38 -
39 - const res = await request(app)
40 - .post("/mood")
41 - .send({
42 - user_id: userId,
43 - stress_level: 3,
44 - anxiety_level: 4,
45 - energy_level: 7,
46 - recorded_at: now
47 - });
48 -
49 - t.is(res.status, 201);
50 - t.truthy(res.body.id);
51 - });
52 -
53 - test("Criar registro de humor com avaliação e descrição", async (t) => {
54 - const now = new Date().toISOString();
55 -
56 - const res = await request(app)
57 - .post("/mood")
58 - .send({
59 - user_id: userId,
60 - rating: 8,
61 - stress_level: 2,
62 - anxiety_level: 3,
63 - energy_level: 9,
64 - title: "Dia produtivo",
65 - description: "Consegui terminar todas as tarefas",
66 - recorded_at: now
67 - });
68 -
69 - t.is(res.status, 201);
70 -
71 - // Verify data was saved correctly
72 - const mood = db.prepare("SELECT * FROM mood WHERE id = ?").get(res.body.id);
73 - t.is(mood.rating, 8);
74 - t.is(mood.stress_level, 2);
75 - t.is(mood.title, "Dia produtivo");
76 - });
77 -
78 - test("Criar registro de humor com componentes emocionais", async (t) => {
79 - const now = new Date().toISOString();
80 -
81 - const res = await request(app)
82 - .post("/mood")
83 - .send({
84 - user_id: userId,
85 - stress_level: 5,
86 - anxiety_level: 6,
87 - energy_level: 4,
88 - recorded_at: now,
89 - mood_components: [
90 - { emotion: "joy", intensity: 7 },
91 - { emotion: "anxiety", intensity: 6 },
92 - { emotion: "trust", intensity: 8 }
93 - ]
94 - });
95 -
96 - t.is(res.status, 201);
97 -
98 - // Verify components were saved
99 - const components = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").all(res.body.id);
100 - t.is(components.length, 3);
101 -
102 - const emotions = components.map(c => c.emotion);
103 - t.true(emotions.includes('joy'));
104 - t.true(emotions.includes('anxiety'));
105 - t.true(emotions.includes('trust'));
106 - });
107 -
108 - test("Componentes emocionais devem aceitar maiúsculas e converter para minúsculas", async (t) => {
109 - const now = new Date().toISOString();
110 -
111 - const res = await request(app)
112 - .post("/mood")
113 - .send({
114 - user_id: userId,
115 - stress_level: 5,
116 - anxiety_level: 6,
117 - energy_level: 4,
118 - recorded_at: now,
119 - mood_components: [
120 - { emotion: "JOY", intensity: 7 },
121 - { emotion: "AnXiEtY", intensity: 6 }
122 - ]
123 - });
124 -
125 - t.is(res.status, 201);
126 -
127 - // Verify emotions were lowercased
128 - const components = db.prepare("SELECT emotion FROM mood_components WHERE mood_id = ? ORDER BY emotion ASC").all(res.body.id);
129 - t.is(components[0].emotion, 'anxiety');
130 - t.is(components[1].emotion, 'joy');
131 - });
132 -
133 - test("Falha ao criar registro de humor sem ID de usuário", async (t) => {
134 - const now = new Date().toISOString();
135 -
136 - const res = await request(app)
137 - .post("/mood")
138 - .send({
139 - stress_level: 3,
140 - anxiety_level: 4,
141 - energy_level: 7,
142 - recorded_at: now
143 - });
144 -
145 - t.is(res.status, 400);
146 - t.is(res.body.error, "Campos faltantes");
147 - t.true(res.body.missing.includes('user_id'));
148 - });
149 -
150 - test("Falha ao criar registro de humor sem nível de stress", async (t) => {
151 - const now = new Date().toISOString();
152 -
153 - const res = await request(app)
154 - .post("/mood")
155 - .send({
156 - user_id: userId,
157 - anxiety_level: 4,
158 - energy_level: 7,
159 - recorded_at: now
160 - });
161 -
162 - t.is(res.status, 400);
163 - t.is(res.body.error, "Campos faltantes");
164 - t.true(res.body.missing.includes('stress_level'));
165 - });
166 -
167 - test("Falha ao criar registro de humor sem nível de ansiedade", async (t) => {
168 - const now = new Date().toISOString();
169 -
170 - const res = await request(app)
171 - .post("/mood")
172 - .send({
173 - user_id: userId,
174 - stress_level: 3,
175 - energy_level: 7,
176 - recorded_at: now
177 - });
178 -
179 - t.is(res.status, 400);
180 - t.is(res.body.error, "Campos faltantes");
181 - t.true(res.body.missing.includes('anxiety_level'));
182 - });
183 -
184 - test("Falha ao criar registro de humor sem nível de energia", async (t) => {
185 - const now = new Date().toISOString();
186 -
187 - const res = await request(app)
188 - .post("/mood")
189 - .send({
190 - user_id: userId,
191 - stress_level: 3,
192 - anxiety_level: 4,
193 - recorded_at: now
194 - });
195 -
196 - t.is(res.status, 400);
197 - t.is(res.body.error, "Campos faltantes");
198 - t.true(res.body.missing.includes('energy_level'));
199 - });
200 -
201 - test("Falha ao criar registro de humor sem data de registro", async (t) => {
202 - const res = await request(app)
203 - .post("/mood")
204 - .send({
205 - user_id: userId,
206 - stress_level: 3,
207 - anxiety_level: 4,
208 - energy_level: 7
209 - });
210 -
211 - t.is(res.status, 400);
212 - t.is(res.body.error, "Campos faltantes");
213 - t.true(res.body.missing.includes('recorded_at'));
214 - });
215 -
216 - test("Falha ao criar registro de humor com nível de estresse inválido (menor que 1)", async (t) => {
217 - const now = new Date().toISOString();
218 -
219 - const res = await request(app)
220 - .post("/mood")
221 - .send({
222 - user_id: userId,
223 - stress_level: 0,
224 - anxiety_level: 4,
225 - energy_level: 7,
226 - recorded_at: now
227 - });
228 -
229 - t.is(res.status, 400);
230 - t.is(res.body.error, "stress_level deve ser entre 1 e 10");
231 - });
232 -
233 - test("Falha ao criar registro de humor com nível de estresse inválido (maior que 10)", async (t) => {
234 - const now = new Date().toISOString();
235 -
236 - const res = await request(app)
237 - .post("/mood")
238 - .send({
239 - user_id: userId,
240 - stress_level: 11,
241 - anxiety_level: 4,
242 - energy_level: 7,
243 - recorded_at: now
244 - });
245 -
246 - t.is(res.status, 400);
247 - t.is(res.body.error, "stress_level deve ser entre 1 e 10");
248 - });
249 -
250 - test("Falha ao criar registro de humor com nível de ansiedade inválido", async (t) => {
251 - const now = new Date().toISOString();
252 -
253 - const res = await request(app)
254 - .post("/mood")
255 - .send({
256 - user_id: userId,
257 - stress_level: 3,
258 - anxiety_level: 15,
259 - energy_level: 7,
260 - recorded_at: now
261 - });
262 -
263 - t.is(res.status, 400);
264 - t.is(res.body.error, "anxiety_level deve ser entre 1 e 10");
265 - });
266 -
267 - test("Falha ao criar registro de humor com nível de energia inválido", async (t) => {
268 - const now = new Date().toISOString();
269 -
270 - const res = await request(app)
271 - .post("/mood")
272 - .send({
273 - user_id: userId,
274 - stress_level: 3,
275 - anxiety_level: 4,
276 - energy_level: -5,
277 - recorded_at: now
278 - });
279 -
280 - t.is(res.status, 400);
281 - t.is(res.body.error, "energy_level deve ser entre 1 e 10");
282 - });
283 -
284 - test("Falha ao criar registro de humor com avaliação inválido", async (t) => {
285 - const now = new Date().toISOString();
286 -
287 - const res = await request(app)
288 - .post("/mood")
289 - .send({
290 - user_id: userId,
291 - rating: 20,
292 - stress_level: 3,
293 - anxiety_level: 4,
294 - energy_level: 7,
295 - recorded_at: now
296 - });
297 -
298 - t.is(res.status, 400);
299 - t.is(res.body.error, "rating deve ser entre 1 e 10");
300 - });
301 -
302 - test("Falha ao criar registro de humor com data inválida", async (t) => {
303 - const res = await request(app)
304 - .post("/mood")
305 - .send({
306 - user_id: userId,
307 - stress_level: 3,
308 - anxiety_level: 4,
309 - energy_level: 7,
310 - recorded_at: "data-invalida"
311 - });
312 -
313 - t.is(res.status, 400);
314 - t.is(res.body.error, "recorded_at deve ser uma data válida");
315 - });
316 -
317 - test("Falha ao criar registro de humor com data no futuro", async (t) => {
318 - const futureDate = new Date();
319 - futureDate.setDate(futureDate.getDate() + 1);
320 -
321 - const res = await request(app)
322 - .post("/mood")
323 - .send({
324 - user_id: userId,
325 - stress_level: 3,
326 - anxiety_level: 4,
327 - energy_level: 7,
328 - recorded_at: futureDate.toISOString()
329 - });
330 -
331 - t.is(res.status, 400);
332 - t.is(res.body.error, "recorded_at não pode ser no futuro");
333 - });
334 -
335 - test("Falha ao criar registro de humor com emoção inválida", async (t) => {
336 - const now = new Date().toISOString();
337 -
338 - const res = await request(app)
339 - .post("/mood")
340 - .send({
341 - user_id: userId,
342 - stress_level: 3,
343 - anxiety_level: 4,
344 - energy_level: 7,
345 - recorded_at: now,
346 - mood_components: [
347 - { emotion: "invalid_emotion", intensity: 5 }
348 - ]
349 - });
350 -
351 - t.is(res.status, 400);
352 - t.regex(res.body.error, /Invalid emotion/);
353 - });
354 -
355 - test("Falha ao criar registro de humor com intensidade inválida (menor que 1)", async (t) => {
356 - const now = new Date().toISOString();
357 -
358 - const res = await request(app)
359 - .post("/mood")
360 - .send({
361 - user_id: userId,
362 - stress_level: 3,
363 - anxiety_level: 4,
364 - energy_level: 7,
365 - recorded_at: now,
366 - mood_components: [
367 - { emotion: "joy", intensity: 0 }
368 - ]
369 - });
370 -
371 - t.is(res.status, 400);
372 - t.regex(res.body.error, /Invalid intensity/);
373 - });
374 -
375 - test("Falha ao criar registro de humor com intensidade inválida (maior que 10)", async (t) => {
376 - const now = new Date().toISOString();
377 -
378 - const res = await request(app)
379 - .post("/mood")
380 - .send({
381 - user_id: userId,
382 - stress_level: 3,
383 - anxiety_level: 4,
384 - energy_level: 7,
385 - recorded_at: now,
386 - mood_components: [
387 - { emotion: "sad", intensity: 11 }
388 - ]
389 - });
390 -
391 - t.is(res.status, 400);
392 - t.regex(res.body.error, /Invalid intensity/);
393 - });
394 -
395 - test("Falha ao criar registro de humor com emoções duplicadas", async (t) => {
396 - const now = new Date().toISOString();
397 -
398 - const res = await request(app)
399 - .post("/mood")
400 - .send({
401 - user_id: userId,
402 - stress_level: 3,
403 - anxiety_level: 4,
404 - energy_level: 7,
405 - recorded_at: now,
406 - mood_components: [
407 - { emotion: "joy", intensity: 7 },
408 - { emotion: "joy", intensity: 5 }
409 - ]
410 - });
411 -
412 - t.is(res.status, 400);
413 - t.regex(res.body.error, /Duplicate emotion/);
414 - });
415 -
416 - test("Falha ao criar registro de humor com componente sem emoção de referencia", async (t) => {
417 - const now = new Date().toISOString();
418 -
419 - const res = await request(app)
420 - .post("/mood")
421 - .send({
422 - user_id: userId,
423 - stress_level: 3,
424 - anxiety_level: 4,
425 - energy_level: 7,
426 - recorded_at: now,
427 - mood_components: [
428 - { intensity: 5 }
429 - ]
430 - });
431 -
432 - t.is(res.status, 400);
433 - t.regex(res.body.error, /must have emotion and intensity/);
434 - });
435 -
436 - test("Falha ao criar registro de humor com componente sem intensidade", async (t) => {
437 - const now = new Date().toISOString();
438 -
439 - const res = await request(app)
440 - .post("/mood")
441 - .send({
442 - user_id: userId,
443 - stress_level: 3,
444 - anxiety_level: 4,
445 - energy_level: 7,
446 - recorded_at: now,
447 - mood_components: [
448 - { emotion: "joy" }
449 - ]
450 - });
451 -
452 - t.is(res.status, 400);
453 - t.regex(res.body.error, /must have emotion and intensity/);
454 - });
455 -
456 - test("Criar registro de humor com lista vazia de componentes deve funcionar", async (t) => {
457 - const now = new Date().toISOString();
458 -
459 - const res = await request(app)
460 - .post("/mood")
461 - .send({
462 - user_id: userId,
463 - stress_level: 3,
464 - anxiety_level: 4,
465 - energy_level: 7,
466 - recorded_at: now,
467 - mood_components: []
468 - });
469 -
470 - t.is(res.status, 201);
471 -
472 - // Verify no components were created
473 - const components = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").all(res.body.id);
474 - t.is(components.length, 0);
475 - });
476 -
477 - test("Descrição e título devem ser sanitizados (truncados em 500 caracteres)", async (t) => {
478 - const now = new Date().toISOString();
479 - const longText = "A".repeat(600);
480 -
481 - const res = await request(app)
482 - .post("/mood")
483 - .send({
484 - user_id: userId,
485 - stress_level: 3,
486 - anxiety_level: 4,
487 - energy_level: 7,
488 - recorded_at: now,
489 - title: longText,
490 - description: longText
491 - });
492 -
493 - t.is(res.status, 201);
494 -
495 - const mood = db.prepare("SELECT title, description FROM mood WHERE id = ?").get(res.body.id);
496 - t.is(mood.title.length, 500);
497 - t.is(mood.description.length, 500);
498 - });
@@ -1,298 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
5 -
6 - let app;
7 - let db;
8 - let userId;
9 - let moodIds = [];
10 -
11 - test.before(() => {
12 - db = setupTestDb();
13 - app = createApp(db);
14 - });
15 -
16 - test.beforeEach(async () => {
17 - cleanupTestDb();
18 - moodIds = [];
19 -
20 - // Create a test user with unique email
21 - const uniqueEmail = `mood_${Date.now()}_${Math.random()}@example.com`;
22 - const userRes = await request(app)
23 - .post("/users")
24 - .send({
25 - email: uniqueEmail,
26 - password: "senha123",
27 - first_name: "Mood",
28 - last_name: "Tester"
29 - });
30 -
31 - if (userRes.status !== 201) {
32 - throw new Error(`Failed to create test user: ${JSON.stringify(userRes.body)}`);
33 - }
34 -
35 - userId = userRes.body.id;
36 -
37 - // Create multiple moods for testing
38 - const dates = [
39 - new Date('2024-01-15T10:00:00Z'),
40 - new Date('2024-01-20T14:30:00Z'),
41 - new Date('2024-02-01T08:00:00Z'),
42 - new Date('2024-02-15T16:45:00Z'),
43 - new Date('2024-03-01T12:00:00Z')
44 - ];
45 -
46 - for (let i = 0; i < dates.length; i++) {
47 - const moodRes = await request(app)
48 - .post("/mood")
49 - .send({
50 - user_id: userId,
51 - rating: 5 + i,
52 - stress_level: 3 + i,
53 - anxiety_level: 2 + i,
54 - energy_level: 6 - i,
55 - title: `Mood ${i + 1}`,
56 - recorded_at: dates[i].toISOString(),
57 - mood_components: i % 2 === 0 ? [
58 - { emotion: "joy", intensity: 5 + i },
59 - { emotion: "trust", intensity: 4 + i }
60 - ] : []
61 - });
62 - moodIds.push(moodRes.body.id);
63 - }
64 - });
65 -
66 - test.serial("Obter mood específico por ID", async (t) => {
67 - const res = await request(app).get(`/mood/${moodIds[0]}`);
68 -
69 - t.is(res.status, 200);
70 - t.is(Number(res.body.id), Number(moodIds[0]));
71 - t.is(Number(res.body.user_id), Number(userId));
72 - t.is(res.body.title, "Mood 1");
73 - t.truthy(res.body.first_name);
74 - t.truthy(res.body.last_name);
75 - t.true(Array.isArray(res.body.mood_components));
76 - });
77 -
78 - test.serial("Mood deve incluir componentes emocionais", async (t) => {
79 - const res = await request(app).get(`/mood/${moodIds[0]}`);
80 -
81 - t.is(res.status, 200);
82 - t.is(res.body.mood_components.length, 2);
83 -
84 - const emotions = res.body.mood_components.map(c => c.emotion);
85 - t.true(emotions.includes('joy'));
86 - t.true(emotions.includes('trust'));
87 - });
88 -
89 - test.serial("Mood deve incluir estatísticas emocionais quando houver componentes", async (t) => {
90 - const res = await request(app).get(`/mood/${moodIds[0]}`);
91 -
92 - t.is(res.status, 200);
93 - t.truthy(res.body.emotion_stats);
94 - t.truthy(res.body.emotion_stats.dominant);
95 - t.truthy(res.body.emotion_stats.average);
96 - t.truthy(res.body.emotion_stats.breakdown);
97 - });
98 -
99 - test.serial("Falha ao obter mood inexistente", async (t) => {
100 - const res = await request(app).get("/mood/99999");
101 -
102 - t.is(res.status, 404);
103 - t.is(res.body.error, "Humor não encontrado");
104 - });
105 -
106 - test.serial("Obter todos os moods de um usuário", async (t) => {
107 - const res = await request(app).get(`/mood/user/${userId}`);
108 -
109 - t.is(res.status, 200);
110 - t.truthy(res.body.moods);
111 - t.is(res.body.moods.length, 5);
112 - t.truthy(res.body.pagination);
113 - t.is(res.body.pagination.total, 5);
114 - });
115 -
116 - test.serial("Moods devem estar ordenados por data (mais recente primeiro)", async (t) => {
117 - const res = await request(app).get(`/mood/user/${userId}`);
118 -
119 - t.is(res.status, 200);
120 -
121 - const dates = res.body.moods.map(m => new Date(m.recorded_at));
122 - for (let i = 1; i < dates.length; i++) {
123 - t.true(dates[i - 1] >= dates[i]);
124 - }
125 - });
126 -
127 - test.serial("Filtrar registros de humor por intervalo de datas", async (t) => {
128 - const res = await request(app).get(`/mood/user/${userId}`)
129 - .query({
130 - start_date: '2024-01-01',
131 - end_date: '2024-01-31'
132 - });
133 -
134 - t.is(res.status, 200);
135 - t.is(res.body.moods.length, 2); // Only January moods
136 - });
137 -
138 - test.serial("Filtrar registros de humor apenas por data de início", async (t) => {
139 - const res = await request(app).get(`/mood/user/${userId}`)
140 - .query({
141 - start_date: '2024-02-01'
142 - });
143 -
144 - t.is(res.status, 200);
145 - t.is(res.body.moods.length, 3); // Feb and March moods
146 - });
147 -
148 - test.serial("Filtrar registros de humor apenas por data de fim", async (t) => {
149 - const res = await request(app).get(`/mood/user/${userId}`)
150 - .query({
151 - end_date: '2024-01-31'
152 - });
153 -
154 - t.is(res.status, 200);
155 - t.is(res.body.moods.length, 2); // Only January moods
156 - });
157 -
158 - test.serial("Paginação de moods", async (t) => {
159 - const res = await request(app).get(`/mood/user/${userId}`)
160 - .query({
161 - page: 2,
162 - limit: 2
163 - });
164 -
165 - t.is(res.status, 200);
166 - t.is(res.body.moods.length, 2);
167 - t.is(res.body.pagination.page, 2);
168 - t.is(res.body.pagination.limit, 2);
169 - t.is(res.body.pagination.total_pages, 3);
170 - });
171 -
172 - test.serial("Paginação com limite máximo de 100", async (t) => {
173 - const res = await request(app).get(`/mood/user/${userId}`)
174 - .query({
175 - limit: 200
176 - });
177 -
178 - t.is(res.status, 200);
179 - t.is(res.body.pagination.limit, 100); // Should be capped at 100
180 - });
181 -
182 - test.serial("Falha ao filtrar com intervalo de datas inválido", async (t) => {
183 - const res = await request(app).get(`/mood/user/${userId}`)
184 - .query({
185 - start_date: '2024-02-01',
186 - end_date: '2024-01-01' // End before start
187 - });
188 -
189 - t.is(res.status, 400);
190 - t.is(res.body.error, "Intervalo de datas inválido");
191 - });
192 -
193 - test.serial("Falha ao filtrar com data de início inválida", async (t) => {
194 - const res = await request(app).get(`/mood/user/${userId}`)
195 - .query({
196 - start_date: 'data-invalida'
197 - });
198 -
199 - t.is(res.status, 400);
200 - t.is(res.body.error, "Data de início inválida");
201 - });
202 -
203 - test.serial("Falha ao filtrar com data de fim inválida", async (t) => {
204 - const res = await request(app).get(`/mood/user/${userId}`)
205 - .query({
206 - end_date: 'data-invalida'
207 - });
208 -
209 - t.is(res.status, 400);
210 - t.is(res.body.error, "Data de fim inválida");
211 - });
212 -
213 - test.serial("Obter estatísticas de mood do usuário", async (t) => {
214 - const res = await request(app).get(`/mood/user/${userId}/stats`);
215 -
216 - t.is(res.status, 200);
217 - t.truthy(res.body.overall);
218 - t.is(res.body.overall.total_entries, 5);
219 - t.truthy(res.body.overall.avg_rating);
220 - t.truthy(res.body.overall.avg_stress);
221 - t.truthy(res.body.overall.avg_anxiety);
222 - t.truthy(res.body.overall.avg_energy);
223 - t.truthy(res.body.emotions);
224 - t.truthy(res.body.trends);
225 - });
226 -
227 - test.serial("Estatísticas devem incluir frequência de emoções", async (t) => {
228 - const res = await request(app).get(`/mood/user/${userId}/stats`);
229 -
230 - t.is(res.status, 200);
231 - t.true(Array.isArray(res.body.emotions));
232 -
233 - // We created 3 moods with components
234 - const joyStats = res.body.emotions.find(e => e.emotion === 'joy');
235 - t.truthy(joyStats);
236 - t.is(joyStats.frequency, 3);
237 - });
238 -
239 - test.serial("Estatísticas com filtro de data", async (t) => {
240 - const res = await request(app).get(`/mood/user/${userId}/stats`)
241 - .query({
242 - start_date: '2024-01-01',
243 - end_date: '2024-01-31'
244 - });
245 -
246 - t.is(res.status, 200);
247 - t.is(res.body.overall.total_entries, 2); // Only January
248 - t.is(res.body.date_range.start, '2024-01-01');
249 - t.is(res.body.date_range.end, '2024-01-31');
250 - });
251 -
252 - test.serial("Estatísticas devem incluir tendências por dia da semana", async (t) => {
253 - const res = await request(app).get(`/mood/user/${userId}/stats`);
254 -
255 - t.is(res.status, 200);
256 - t.true(Array.isArray(res.body.trends.by_day_of_week));
257 -
258 - const hasDay = res.body.trends.by_day_of_week.some(d =>
259 - ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'].includes(d.day_of_week)
260 - );
261 - t.true(hasDay);
262 - });
263 -
264 - test.serial("Usuário sem moods deve retornar lista vazia", async (t) => {
265 - // Create another user without moods
266 - const newUserRes = await request(app)
267 - .post("/users")
268 - .send({
269 - email: "nomood@example.com",
270 - password: "senha123",
271 - first_name: "No",
272 - last_name: "Mood"
273 - });
274 -
275 - const res = await request(app).get(`/mood/user/${newUserRes.body.id}`);
276 -
277 - t.is(res.status, 200);
278 - t.is(res.body.moods.length, 0);
279 - t.is(res.body.pagination.total, 0);
280 - });
281 -
282 - test.serial("Estatísticas de usuário sem moods", async (t) => {
283 - // Create another user without moods
284 - const newUserRes = await request(app)
285 - .post("/users")
286 - .send({
287 - email: "nostats@example.com",
288 - password: "senha123",
289 - first_name: "No",
290 - last_name: "Stats"
291 - });
292 -
293 - const res = await request(app).get(`/mood/user/${newUserRes.body.id}/stats`);
294 -
295 - t.is(res.status, 200);
296 - t.is(res.body.overall.total_entries, 0);
297 - t.is(res.body.emotions.length, 0);
298 - });
@@ -1,349 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
5 -
6 - let app;
7 - let db;
8 - let userId;
9 - let moodId;
10 -
11 - test.before(() => {
12 - db = setupTestDb();
13 - app = createApp(db);
14 - });
15 -
16 - test.beforeEach(async () => {
17 - cleanupTestDb();
18 -
19 - // Create a test user with unique email
20 - const uniqueEmail = `mood_${Date.now()}_${Math.random()}@example.com`;
21 - const userRes = await request(app)
22 - .post("/users")
23 - .send({
24 - email: uniqueEmail,
25 - password: "senha123",
26 - first_name: "Mood",
27 - last_name: "Tester"
28 - });
29 -
30 - if (userRes.status !== 201) {
31 - throw new Error(`Failed to create test user: ${JSON.stringify(userRes.body)}`);
32 - }
33 -
34 - userId = userRes.body.id;
35 -
36 - // Create a mood to update
37 - const moodRes = await request(app)
38 - .post("/mood")
39 - .send({
40 - user_id: userId,
41 - rating: 5,
42 - stress_level: 4,
43 - anxiety_level: 3,
44 - energy_level: 7,
45 - title: "Original mood",
46 - description: "Original description",
47 - recorded_at: new Date().toISOString(),
48 - mood_components: [
49 - { emotion: "joy", intensity: 6 },
50 - { emotion: "trust", intensity: 7 }
51 - ]
52 - });
53 -
54 - moodId = moodRes.body.id;
55 - });
56 -
57 - test.serial("Atualizar rating do mood", async (t) => {
58 - const res = await request(app)
59 - .put(`/mood/${moodId}`)
60 - .send({
61 - rating: 8
62 - });
63 -
64 - t.is(res.status, 200);
65 - t.true(res.body.success);
66 -
67 - // Verify update
68 - const mood = db.prepare("SELECT rating FROM mood WHERE id = ?").get(moodId);
69 - t.is(mood.rating, 8);
70 - });
71 -
72 - test.serial("Atualizar múltiplos níveis do mood", async (t) => {
73 - const res = await request(app)
74 - .put(`/mood/${moodId}`)
75 - .send({
76 - stress_level: 2,
77 - anxiety_level: 1,
78 - energy_level: 9
79 - });
80 -
81 - t.is(res.status, 200);
82 - t.true(res.body.success);
83 -
84 - // Verify updates
85 - const mood = db.prepare("SELECT stress_level, anxiety_level, energy_level FROM mood WHERE id = ?").get(moodId);
86 - t.is(mood.stress_level, 2);
87 - t.is(mood.anxiety_level, 1);
88 - t.is(mood.energy_level, 9);
89 - });
90 -
91 - test.serial("Atualizar título e descrição do mood", async (t) => {
92 - const res = await request(app)
93 - .put(`/mood/${moodId}`)
94 - .send({
95 - title: "Novo título",
96 - description: "Nova descrição detalhada"
97 - });
98 -
99 - t.is(res.status, 200);
100 - t.true(res.body.success);
101 -
102 - // Verify updates
103 - const mood = db.prepare("SELECT title, description FROM mood WHERE id = ?").get(moodId);
104 - t.is(mood.title, "Novo título");
105 - t.is(mood.description, "Nova descrição detalhada");
106 - });
107 -
108 - test.serial("Atualizar data do mood", async (t) => {
109 - const newDate = new Date('2024-01-15T10:00:00Z');
110 -
111 - const res = await request(app)
112 - .put(`/mood/${moodId}`)
113 - .send({
114 - recorded_at: newDate.toISOString()
115 - });
116 -
117 - t.is(res.status, 200);
118 - t.true(res.body.success);
119 -
120 - // Verify update
121 - const mood = db.prepare("SELECT recorded_at FROM mood WHERE id = ?").get(moodId);
122 - t.truthy(mood.recorded_at.includes('2024-01-15'));
123 - });
124 -
125 - test.serial("Atualizar componentes emocionais do mood", async (t) => {
126 - const res = await request(app)
127 - .put(`/mood/${moodId}`)
128 - .send({
129 - mood_components: [
130 - { emotion: "sad", intensity: 4 },
131 - { emotion: "fear", intensity: 3 },
132 - { emotion: "angry", intensity: 2 }
133 - ]
134 - });
135 -
136 - t.is(res.status, 200);
137 - t.true(res.body.success);
138 -
139 - // Verify old components were replaced
140 - const components = db.prepare("SELECT emotion, intensity FROM mood_components WHERE mood_id = ? ORDER BY emotion").all(moodId);
141 - t.is(components.length, 3);
142 -
143 - const emotions = components.map(c => c.emotion);
144 - t.true(emotions.includes('sad'));
145 - t.true(emotions.includes('fear'));
146 - t.true(emotions.includes('angry'));
147 - t.false(emotions.includes('joy')); // Old emotion should be gone
148 - t.false(emotions.includes('trust')); // Old emotion should be gone
149 - });
150 -
151 - test.serial("Remover todos os componentes emocionais enviando array vazio", async (t) => {
152 - const res = await request(app)
153 - .put(`/mood/${moodId}`)
154 - .send({
155 - mood_components: []
156 - });
157 -
158 - t.is(res.status, 200);
159 - t.true(res.body.success);
160 -
161 - // Verify components were deleted
162 - const components = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").all(moodId);
163 - t.is(components.length, 0);
164 - });
165 -
166 - test.serial("Atualização deve modificar a coluna de atualização", async (t) => {
167 - // Get original updated_at
168 - const originalMood = db.prepare("SELECT updated_at FROM mood WHERE id = ?")
169 - .get(moodId);
170 -
171 - // Wait a bit to ensure timestamp difference
172 - await new Promise(resolve => setTimeout(resolve, 1100));
173 -
174 - const res = await request(app)
175 - .put(`/mood/${moodId}`)
176 - .send({
177 - rating: 9
178 - });
179 -
180 - t.is(res.status, 200);
181 -
182 - // Verify updated_at changed
183 - const updatedMood = db.prepare("SELECT updated_at FROM mood WHERE id = ?")
184 - .get(moodId);
185 -
186 - t.not(updatedMood.updated_at, originalMood.updated_at);
187 - });
188 -
189 - test.serial("Falha ao atualizar humor inexistente", async (t) => {
190 - const res = await request(app)
191 - .put("/mood/99999")
192 - .send({
193 - rating: 8
194 - });
195 -
196 - t.is(res.status, 404);
197 - t.is(res.body.error, "Humor não encontrado");
198 - });
199 -
200 - test.serial("Falha ao atualizar com avaliação inválida", async (t) => {
201 - const res = await request(app)
202 - .put(`/mood/${moodId}`)
203 - .send({
204 - rating: 15
205 - });
206 -
207 - t.is(res.status, 400);
208 - t.is(res.body.error, "rating deve ser entre 1 e 10");
209 - });
210 -
211 - test.serial("Falha ao atualizar com nível de estresse inválido", async (t) => {
212 - const res = await request(app)
213 - .put(`/mood/${moodId}`)
214 - .send({
215 - stress_level: 0
216 - });
217 -
218 - t.is(res.status, 400);
219 - t.is(res.body.error, "stress_level deve ser entre 1 e 10");
220 - });
221 -
222 - test.serial("Falha ao atualizar com nível de ansiedade inválido", async (t) => {
223 - const res = await request(app)
224 - .put(`/mood/${moodId}`)
225 - .send({
226 - anxiety_level: 11
227 - });
228 -
229 - t.is(res.status, 400);
230 - t.is(res.body.error, "anxiety_level deve ser entre 1 e 10");
231 - });
232 -
233 - test.serial("Falha ao atualizar com nível de energia inválida", async (t) => {
234 - const res = await request(app)
235 - .put(`/mood/${moodId}`)
236 - .send({
237 - energy_level: -1
238 - });
239 -
240 - t.is(res.status, 400);
241 - t.is(res.body.error, "energy_level deve ser entre 1 e 10");
242 - });
243 -
244 - test.serial("Falha ao atualizar com data inválida", async (t) => {
245 - const res = await request(app)
246 - .put(`/mood/${moodId}`)
247 - .send({
248 - recorded_at: "data-invalida"
249 - });
250 -
251 - t.is(res.status, 400);
252 - t.is(res.body.error, "recorded_at deve ser uma data válida");
253 - });
254 -
255 - test.serial("Falha ao atualizar com data no futuro", async (t) => {
256 - const futureDate = new Date();
257 - futureDate.setDate(futureDate.getDate() + 1);
258 -
259 - const res = await request(app)
260 - .put(`/mood/${moodId}`)
261 - .send({
262 - recorded_at: futureDate.toISOString()
263 - });
264 -
265 - t.is(res.status, 400);
266 - t.is(res.body.error, "recorded_at não pode ser no futuro");
267 - });
268 -
269 - test.serial("Falha ao atualizar com emoção inválida nos componentes", async (t) => {
270 - const res = await request(app)
271 - .put(`/mood/${moodId}`)
272 - .send({
273 - mood_components: [
274 - { emotion: "invalid", intensity: 5 }
275 - ]
276 - });
277 -
278 - t.is(res.status, 400);
279 - t.regex(res.body.error, /Invalid emotion/);
280 - });
281 -
282 - test.serial("Falha ao atualizar com emoções duplicadas", async (t) => {
283 - const res = await request(app)
284 - .put(`/mood/${moodId}`)
285 - .send({
286 - mood_components: [
287 - { emotion: "joy", intensity: 5 },
288 - { emotion: "joy", intensity: 7 }
289 - ]
290 - });
291 -
292 - t.is(res.status, 400);
293 - t.regex(res.body.error, /Duplicate emotion/);
294 - });
295 -
296 - test.serial("Deletar humor existente", async (t) => {
297 - const res = await request(app).delete(`/mood/${moodId}`);
298 -
299 - t.is(res.status, 200);
300 - t.true(res.body.success);
301 - t.is(Number(res.body.deleted), Number(moodId));
302 -
303 - // Verify mood is deleted
304 - const mood = db.prepare("SELECT * FROM mood WHERE id = ?").get(moodId);
305 - t.falsy(mood);
306 - });
307 -
308 - test.serial("Deletar humor deve deletar seus componentes (em cascata)", async (t) => {
309 - // Verify components exist
310 - const componentsBefore = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").all(moodId);
311 - t.true(componentsBefore.length > 0);
312 -
313 - const res = await request(app).delete(`/mood/${moodId}`);
314 - t.is(res.status, 200);
315 -
316 - // Verify components are deleted
317 - const componentsAfter = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").all(moodId);
318 - t.is(componentsAfter.length, 0);
319 - });
320 -
321 - test.serial("Falha ao deletar mood inexistente", async (t) => {
322 - const res = await request(app).delete("/mood/99999");
323 -
324 - t.is(res.status, 404);
325 - t.is(res.body.error, "Humor não encontrado");
326 - });
327 -
328 - test.serial("Atualização parcial mantém valores não alterados", async (t) => {
329 - // Get original values
330 - const originalMood = db.prepare("SELECT * FROM mood WHERE id = ?").get(moodId);
331 -
332 - // Update only rating
333 - const res = await request(app)
334 - .put(`/mood/${moodId}`)
335 - .send({
336 - rating: 10
337 - });
338 -
339 - t.is(res.status, 200);
340 -
341 - // Verify only rating changed, others remained
342 - const updatedMood = db.prepare("SELECT * FROM mood WHERE id = ?").get(moodId);
343 - t.is(updatedMood.rating, 10);
344 - t.is(updatedMood.stress_level, originalMood.stress_level);
345 - t.is(updatedMood.anxiety_level, originalMood.anxiety_level);
346 - t.is(updatedMood.energy_level, originalMood.energy_level);
347 - t.is(updatedMood.title, originalMood.title);
348 - t.is(updatedMood.description, originalMood.description);
349 - });
@@ -1,33 +0,0 @@
1 - import { setupTestDatabase } from '../src/setup-db.js';
2 -
3 - // Store a single test database instance
4 - let globalTestDb = null;
5 -
6 - // Setup the test database
7 - export function setupTestDb() {
8 - if (!globalTestDb) {
9 - globalTestDb = setupTestDatabase();
10 - }
11 - return globalTestDb;
12 - }
13 -
14 - // Get the test database
15 - export function getTestDb() {
16 - return globalTestDb;
17 - }
18 -
19 - // Cleanup test database
20 - export function cleanupTestDb() {
21 - if (globalTestDb) {
22 - try {
23 - // Delete in correct order due to foreign keys
24 - globalTestDb.exec(`
25 - DELETE FROM mood_components;
26 - DELETE FROM mood;
27 - DELETE FROM users;
28 - `);
29 - } catch (err) {
30 - console.error('Error cleaning test DB:', err);
31 - }
32 - }
33 - }
api/tests/test-helper.ts
@@ -0,0 +1,53 @@
1 + import { PrismaClient } from '@prisma/client';
2 + import { PrismaPg } from '@prisma/adapter-pg';
3 + import 'dotenv/config';
4 +
5 + let prismaTestClient: PrismaClient | null = null;
6 +
7 + export function getTestPrisma(): PrismaClient {
8 + if (!prismaTestClient) {
9 + // Use test database URL
10 + const connectionString = process.env.DATABASE_URL || 'postgres://user:password@localhost:5430/orbit_test';
11 + const adapter = new PrismaPg({ connectionString });
12 + prismaTestClient = new PrismaClient({ adapter });
13 + }
14 + return prismaTestClient;
15 + }
16 +
17 + export async function cleanupTestDb() {
18 + const prisma = getTestPrisma();
19 +
20 + // Delete in correct order due to foreign keys
21 + // Add more tables as they're added to schema
22 + try {
23 + // When we add more models, delete them here in reverse order of dependencies
24 + await prisma.user.deleteMany({});
25 + } catch (err) {
26 + console.error('Error cleaning test DB:', err);
27 + }
28 + }
29 +
30 + export async function disconnectTestDb() {
31 + if (prismaTestClient) {
32 + await prismaTestClient.$disconnect();
33 + prismaTestClient = null;
34 + }
35 + }
36 +
37 + // Helper to create test users
38 + export async function createTestUser(data?: {
39 + email?: string;
40 + firstName?: string;
41 + lastName?: string;
42 + encryptedPassword?: string;
43 + }) {
44 + const prisma = getTestPrisma();
45 + return await prisma.user.create({
46 + data: {
47 + email: data?.email || 'test@example.com',
48 + firstName: data?.firstName || 'Test',
49 + lastName: data?.lastName || 'User',
50 + encryptedPassword: data?.encryptedPassword || '$2a$10$test'
51 + }
52 + });
53 + }
@@ -1,68 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "./test-helper.js";
5 -
6 - let app;
7 - let db;
8 -
9 - test.before(() => {
10 - db = setupTestDb();
11 - app = createApp(db);
12 - });
13 -
14 - test.beforeEach(() => {
15 - cleanupTestDb();
16 - });
17 -
18 - test("Criar usuário", async (t) => {
19 - const res = await request(app)
20 - .post("/users")
21 - .send({
22 - email: "john@example.com",
23 - password: "123456",
24 - first_name: "John",
25 - last_name: "Doe",
26 - });
27 -
28 - t.is(res.status, 201);
29 - t.truthy(res.body.id);
30 - });
31 -
32 - test("Falha ao criar usuário sem email", async (t) => {
33 - const res = await request(app)
34 - .post("/users")
35 - .send({
36 - password: "123456",
37 - first_name: "John",
38 - last_name: "Doe",
39 - });
40 -
41 - t.is(res.status, 400);
42 - t.is(res.body.error, "Campos faltantes");
43 - });
44 -
45 - test("Falha ao criar usuário com email duplicado", async (t) => {
46 - // First user
47 - await request(app)
48 - .post("/users")
49 - .send({
50 - email: "duplicate@example.com",
51 - password: "123456",
52 - first_name: "John",
53 - last_name: "Doe",
54 - });
55 -
56 - // Duplicate email
57 - const res = await request(app)
58 - .post("/users")
59 - .send({
60 - email: "duplicate@example.com",
61 - password: "123456",
62 - first_name: "Jane",
63 - last_name: "Smith",
64 - });
65 -
66 - t.is(res.status, 400);
67 - t.is(res.body.error, "Email já cadastrado");
68 - });
@@ -1,148 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import bcrypt from "bcryptjs";
4 - import { createApp } from "../../src/create-app.js";
5 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
6 -
7 - let app;
8 - let db;
9 -
10 - test.before(() => {
11 - db = setupTestDb();
12 - app = createApp(db);
13 - });
14 -
15 - test.beforeEach(() => {
16 - cleanupTestDb();
17 -
18 - // Create a test user for login tests
19 - const hashedPassword = bcrypt.hashSync("senha123", 10);
20 - db.prepare(`
21 - INSERT INTO users (email, password, first_name, last_name, timezone)
22 - VALUES (?, ?, ?, ?, ?)
23 - `).run("teste@example.com", hashedPassword, "Teste", "User", "UTC");
24 - });
25 -
26 - test("Login bem-sucedido com credenciais corretas", async (t) => {
27 - const res = await request(app)
28 - .post("/users/login")
29 - .send({
30 - email: "teste@example.com",
31 - password: "senha123"
32 - });
33 -
34 - t.is(res.status, 200);
35 - t.true(res.body.success);
36 - t.truthy(res.body.user);
37 - t.is(res.body.user.email, "teste@example.com");
38 - t.is(res.body.user.first_name, "Teste");
39 - t.is(res.body.user.last_name, "User");
40 - t.falsy(res.body.user.password); // Password should not be in response
41 - });
42 -
43 - test("Login com email em maiúsculas deve funcionar", async (t) => {
44 - const res = await request(app)
45 - .post("/users/login")
46 - .send({
47 - email: "TESTE@EXAMPLE.COM",
48 - password: "senha123"
49 - });
50 -
51 - t.is(res.status, 200);
52 - t.true(res.body.success);
53 - });
54 -
55 - test("Falha no login com senha incorreta", async (t) => {
56 - const res = await request(app)
57 - .post("/users/login")
58 - .send({
59 - email: "teste@example.com",
60 - password: "senhaerrada"
61 - });
62 -
63 - t.is(res.status, 401);
64 - t.is(res.body.error, "Credenciais inválidas");
65 - });
66 -
67 - test("Falha no login com email não cadastrado", async (t) => {
68 - const res = await request(app)
69 - .post("/users/login")
70 - .send({
71 - email: "naocadastrado@example.com",
72 - password: "senha123"
73 - });
74 -
75 - t.is(res.status, 401);
76 - t.is(res.body.error, "Credenciais inválidas");
77 - });
78 -
79 - test("Falha no login sem email", async (t) => {
80 - const res = await request(app)
81 - .post("/users/login")
82 - .send({
83 - password: "senha123"
84 - });
85 -
86 - t.is(res.status, 400);
87 - t.is(res.body.error, "Email e senha são obrigatórios");
88 - });
89 -
90 - test("Falha no login sem senha", async (t) => {
91 - const res = await request(app)
92 - .post("/users/login")
93 - .send({
94 - email: "teste@example.com"
95 - });
96 -
97 - t.is(res.status, 400);
98 - t.is(res.body.error, "Email e senha são obrigatórios");
99 - });
100 -
101 - test("Falha no login sem credenciais", async (t) => {
102 - const res = await request(app)
103 - .post("/users/login")
104 - .send({});
105 -
106 - t.is(res.status, 400);
107 - t.is(res.body.error, "Email e senha são obrigatórios");
108 - });
109 -
110 - test("Login não deve retornar a senha do usuário", async (t) => {
111 - const res = await request(app)
112 - .post("/users/login")
113 - .send({
114 - email: "teste@example.com",
115 - password: "senha123"
116 - });
117 -
118 - t.is(res.status, 200);
119 - t.falsy(res.body.user.password);
120 - t.is(typeof res.body.user.password, 'undefined');
121 - });
122 -
123 - test("Login deve retornar informações completas do usuário", async (t) => {
124 - const res = await request(app)
125 - .post("/users/login")
126 - .send({
127 - email: "teste@example.com",
128 - password: "senha123"
129 - });
130 -
131 - t.is(res.status, 200);
132 - t.truthy(res.body.user.id);
133 - t.is(res.body.user.email, "teste@example.com");
134 - t.is(res.body.user.first_name, "Teste");
135 - t.is(res.body.user.last_name, "User");
136 - t.is(res.body.user.timezone, "UTC");
137 - });
138 -
139 - test("Login com espaços em branco deve ser tratado corretamente", async (t) => {
140 - const res = await request(app)
141 - .post("/users/login")
142 - .send({
143 - email: " teste@example.com ",
144 - password: "senha123"
145 - });
146 -
147 - t.is(res.status, 401); // Should fail as email with spaces doesn't match
148 - });
@@ -1,258 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
5 -
6 - let app;
7 - let db;
8 -
9 - test.before(() => {
10 - db = setupTestDb();
11 - app = createApp(db);
12 - });
13 -
14 - test.beforeEach(() => {
15 - cleanupTestDb();
16 - });
17 -
18 - test("Criar usuário com todos os campos válidos", async (t) => {
19 - const res = await request(app)
20 - .post("/users")
21 - .send({
22 - email: "usuario@example.com",
23 - password: "senha123",
24 - first_name: "João",
25 - last_name: "Silva",
26 - timezone: "America/Sao_Paulo"
27 - });
28 -
29 - t.is(res.status, 201);
30 - t.truthy(res.body.id);
31 - });
32 -
33 - test("Criar usuário com timezone padrão (UTC)", async (t) => {
34 - const res = await request(app)
35 - .post("/users")
36 - .send({
37 - email: "utc@example.com",
38 - password: "senha123",
39 - first_name: "Maria",
40 - last_name: "Santos"
41 - });
42 -
43 - t.is(res.status, 201);
44 -
45 - // Verify timezone was set to UTC
46 - const user = db.prepare("SELECT timezone FROM users WHERE id = ?").get(res.body.id);
47 - t.is(user.timezone, 'UTC');
48 - });
49 -
50 - test("Falha ao criar usuário sem email", async (t) => {
51 - const res = await request(app)
52 - .post("/users")
53 - .send({
54 - password: "senha123",
55 - first_name: "João",
56 - last_name: "Silva"
57 - });
58 -
59 - t.is(res.status, 400);
60 - t.is(res.body.error, "Campos faltantes");
61 - t.true(res.body.missing.includes('email'));
62 - });
63 -
64 - test("Falha ao criar usuário sem senha", async (t) => {
65 - const res = await request(app)
66 - .post("/users")
67 - .send({
68 - email: "usuario@example.com",
69 - first_name: "João",
70 - last_name: "Silva"
71 - });
72 -
73 - t.is(res.status, 400);
74 - t.is(res.body.error, "Campos faltantes");
75 - t.true(res.body.missing.includes('password'));
76 - });
77 -
78 - test("Falha ao criar usuário sem primeiro nome", async (t) => {
79 - const res = await request(app)
80 - .post("/users")
81 - .send({
82 - email: "usuario@example.com",
83 - password: "senha123",
84 - last_name: "Silva"
85 - });
86 -
87 - t.is(res.status, 400);
88 - t.is(res.body.error, "Campos faltantes");
89 - t.true(res.body.missing.includes('first_name'));
90 - });
91 -
92 - test("Falha ao criar usuário sem último nome", async (t) => {
93 - const res = await request(app)
94 - .post("/users")
95 - .send({
96 - email: "usuario@example.com",
97 - password: "senha123",
98 - first_name: "João"
99 - });
100 -
101 - t.is(res.status, 400);
102 - t.is(res.body.error, "Campos faltantes");
103 - t.true(res.body.missing.includes('last_name'));
104 - });
105 -
106 - test("Falha ao criar usuário com múltiplos campos faltantes", async (t) => {
107 - const res = await request(app)
108 - .post("/users")
109 - .send({
110 - first_name: "João"
111 - });
112 -
113 - t.is(res.status, 400);
114 - t.is(res.body.error, "Campos faltantes");
115 - t.true(res.body.missing.includes('email'));
116 - t.true(res.body.missing.includes('password'));
117 - t.true(res.body.missing.includes('last_name'));
118 - });
119 -
120 - test("Falha ao criar usuário com email inválido (sem @)", async (t) => {
121 - const res = await request(app)
122 - .post("/users")
123 - .send({
124 - email: "emailinvalido",
125 - password: "senha123",
126 - first_name: "João",
127 - last_name: "Silva"
128 - });
129 -
130 - t.is(res.status, 400);
131 - t.is(res.body.error, "Email inválido");
132 - });
133 -
134 - test("Falha ao criar usuário com email inválido (sem domínio)", async (t) => {
135 - const res = await request(app)
136 - .post("/users")
137 - .send({
138 - email: "usuario@",
139 - password: "senha123",
140 - first_name: "João",
141 - last_name: "Silva"
142 - });
143 -
144 - t.is(res.status, 400);
145 - t.is(res.body.error, "Email inválido");
146 - });
147 -
148 - test("Falha ao criar usuário com email inválido (formato incorreto)", async (t) => {
149 - const res = await request(app)
150 - .post("/users")
151 - .send({
152 - email: "@example.com",
153 - password: "senha123",
154 - first_name: "João",
155 - last_name: "Silva"
156 - });
157 -
158 - t.is(res.status, 400);
159 - t.is(res.body.error, "Email inválido");
160 - });
161 -
162 - test("Falha ao criar usuário com senha muito curta (menos de 6 caracteres)", async (t) => {
163 - const res = await request(app)
164 - .post("/users")
165 - .send({
166 - email: "usuario@example.com",
167 - password: "123",
168 - first_name: "João",
169 - last_name: "Silva"
170 - });
171 -
172 - t.is(res.status, 400);
173 - t.is(res.body.error, "Senha deve ter pelo menos 6 caracteres");
174 - });
175 -
176 - test("Falha ao criar usuário com senha vazia", async (t) => {
177 - const res = await request(app)
178 - .post("/users")
179 - .send({
180 - email: "usuario@example.com",
181 - password: "",
182 - first_name: "João",
183 - last_name: "Silva"
184 - });
185 -
186 - t.is(res.status, 400);
187 - t.is(res.body.error, "Campos faltantes");
188 - });
189 -
190 - test("Falha ao criar usuário com email duplicado", async (t) => {
191 - // Create first user
192 - await request(app)
193 - .post("/users")
194 - .send({
195 - email: "duplicado@example.com",
196 - password: "senha123",
197 - first_name: "João",
198 - last_name: "Silva"
199 - });
200 -
201 - // Try to create with same email
202 - const res = await request(app)
203 - .post("/users")
204 - .send({
205 - email: "duplicado@example.com",
206 - password: "outrasenha",
207 - first_name: "Maria",
208 - last_name: "Santos"
209 - });
210 -
211 - t.is(res.status, 400);
212 - t.is(res.body.error, "Email já cadastrado");
213 - });
214 -
215 - test("Email deve ser case-insensitive para duplicação", async (t) => {
216 - // Create with lowercase
217 - await request(app)
218 - .post("/users")
219 - .send({
220 - email: "usuario@example.com",
221 - password: "senha123",
222 - first_name: "João",
223 - last_name: "Silva"
224 - });
225 -
226 - // Try with uppercase
227 - const res = await request(app)
228 - .post("/users")
229 - .send({
230 - email: "USUARIO@EXAMPLE.COM",
231 - password: "senha123",
232 - first_name: "Maria",
233 - last_name: "Santos"
234 - });
235 -
236 - t.is(res.status, 400);
237 - t.is(res.body.error, "Email já cadastrado");
238 - });
239 -
240 - test("Sanitização de strings longas no nome", async (t) => {
241 - const longName = "A".repeat(600); // 600 characters
242 -
243 - const res = await request(app)
244 - .post("/users")
245 - .send({
246 - email: "longname@example.com",
247 - password: "senha123",
248 - first_name: longName,
249 - last_name: longName
250 - });
251 -
252 - t.is(res.status, 201);
253 -
254 - // Check that name was truncated to 500 chars
255 - const user = db.prepare("SELECT first_name, last_name FROM users WHERE id = ?").get(res.body.id);
256 - t.is(user.first_name.length, 500);
257 - t.is(user.last_name.length, 500);
258 - });
@@ -1,246 +0,0 @@
1 - import test from "ava";
2 - import request from "supertest";
3 - import { createApp } from "../../src/create-app.js";
4 - import { setupTestDb, cleanupTestDb } from "../test-helper.js";
5 -
6 - let app;
7 - let db;
8 - let userId;
9 -
10 - test.before(() => {
11 - db = setupTestDb();
12 - app = createApp(db);
13 - });
14 -
15 - let testEmail;
16 -
17 - test.serial.beforeEach(async () => {
18 - cleanupTestDb();
19 -
20 - // Create a test user with unique email
21 - testEmail = `perfil_${Date.now()}_${Math.random()}@example.com`;
22 - const res = await request(app)
23 - .post("/users")
24 - .send({
25 - email: testEmail,
26 - password: "senha123",
27 - first_name: "Perfil",
28 - last_name: "Teste",
29 - timezone: "America/Sao_Paulo"
30 - });
31 -
32 - if (res.status !== 201) {
33 - throw new Error(`Failed to create test user: ${JSON.stringify(res.body)}`);
34 - }
35 -
36 - userId = res.body.id;
37 - });
38 -
39 - test.serial("Obter perfil de usuário existente", async (t) => {
40 - const res = await request(app).get(`/users/${userId}`);
41 -
42 - t.is(res.status, 200);
43 - t.is(Number(res.body.id), Number(userId));
44 - t.is(res.body.email, testEmail);
45 - t.is(res.body.first_name, "Perfil");
46 - t.is(res.body.last_name, "Teste");
47 - t.is(res.body.timezone, "America/Sao_Paulo");
48 - t.truthy(res.body.created_at);
49 - t.truthy(res.body.updated_at);
50 - t.is(typeof res.body.total_moods, 'number');
51 - t.falsy(res.body.password); // Should not include password
52 - });
53 -
54 - test.serial("Perfil deve incluir contagem de registros de humor", async (t) => {
55 - // Add some moods
56 - const now = new Date().toISOString();
57 -
58 - await db.prepare(`
59 - INSERT INTO mood (user_id, rating, stress_level, anxiety_level, energy_level, recorded_at)
60 - VALUES (?, ?, ?, ?, ?, ?)
61 - `).run(userId, 7, 3, 4, 8, now);
62 -
63 - await db.prepare(`
64 - INSERT INTO mood (user_id, rating, stress_level, anxiety_level, energy_level, recorded_at)
65 - VALUES (?, ?, ?, ?, ?, ?)
66 - `).run(userId, 5, 6, 7, 4, now);
67 -
68 - const res = await request(app).get(`/users/${userId}`);
69 -
70 - t.is(res.status, 200);
71 - t.is(res.body.total_moods, 2);
72 - });
73 -
74 - test.serial("Falha ao obter perfil de usuário inexistente", async (t) => {
75 - const res = await request(app).get("/users/99999");
76 -
77 - t.is(res.status, 404);
78 - t.is(res.body.error, "Usuário não encontrado");
79 - });
80 -
81 - test.serial("Atualizar email do usuário", async (t) => {
82 - const res = await request(app)
83 - .put(`/users/${userId}`)
84 - .send({
85 - email: "novoemail@example.com"
86 - });
87 -
88 - t.is(res.status, 200);
89 - t.true(res.body.success);
90 -
91 - // Verify update
92 - const user = db.prepare("SELECT email FROM users WHERE id = ?").get(userId);
93 - t.is(user.email, "novoemail@example.com");
94 - });
95 -
96 - test.serial("Atualizar senha do usuário", async (t) => {
97 - const res = await request(app)
98 - .put(`/users/${userId}`)
99 - .send({
100 - password: "novasenha123"
101 - });
102 -
103 - t.is(res.status, 200);
104 - t.true(res.body.success);
105 -
106 - // Verify can login with new password
107 - const loginRes = await request(app)
108 - .post("/users/login")
109 - .send({
110 - email: testEmail,
111 - password: "novasenha123"
112 - });
113 -
114 - t.is(loginRes.status, 200);
115 - });
116 -
117 - test.serial("Atualizar múltiplos campos do usuário", async (t) => {
118 - const res = await request(app)
119 - .put(`/users/${userId}`)
120 - .send({
121 - first_name: "NovoNome",
122 - last_name: "NovoSobrenome",
123 - timezone: "Europe/London"
124 - });
125 -
126 - t.is(res.status, 200);
127 - t.true(res.body.success);
128 -
129 - // Verify updates
130 - const user = db.prepare("SELECT * FROM users WHERE id = ?").get(userId);
131 - t.is(user.first_name, "NovoNome");
132 - t.is(user.last_name, "NovoSobrenome");
133 - t.is(user.timezone, "Europe/London");
134 - });
135 -
136 - test.serial("Falha ao atualizar com email inválido", async (t) => {
137 - const res = await request(app)
138 - .put(`/users/${userId}`)
139 - .send({
140 - email: "emailinvalido"
141 - });
142 -
143 - t.is(res.status, 400);
144 - t.is(res.body.error, "Email inválido");
145 - });
146 -
147 - test.serial("Falha ao atualizar com senha muito curta", async (t) => {
148 - const res = await request(app)
149 - .put(`/users/${userId}`)
150 - .send({
151 - password: "123"
152 - });
153 -
154 - t.is(res.status, 400);
155 - t.is(res.body.error, "Senha deve ter pelo menos 6 caracteres");
156 - });
157 -
158 - test.serial("Falha ao atualizar email para um já existente", async (t) => {
159 - // Create another user
160 - await request(app)
161 - .post("/users")
162 - .send({
163 - email: "outro@example.com",
164 - password: "senha123",
165 - first_name: "Outro",
166 - last_name: "Usuario"
167 - });
168 -
169 - // Try to update to existing email
170 - const res = await request(app)
171 - .put(`/users/${userId}`)
172 - .send({
173 - email: "outro@example.com"
174 - });
175 -
176 - t.is(res.status, 400);
177 - t.is(res.body.error, "Email já está em uso");
178 - });
179 -
180 - test.serial("Falha ao atualizar usuário inexistente", async (t) => {
181 - const res = await request(app)
182 - .put("/users/99999")
183 - .send({
184 - first_name: "Novo"
185 - });
186 -
187 - t.is(res.status, 404);
188 - t.is(res.body.error, "Usuário não encontrado");
189 - });
190 -
191 - test.serial("Falha ao atualizar sem campos", async (t) => {
192 - const res = await request(app)
193 - .put(`/users/${userId}`)
194 - .send({});
195 -
196 - t.is(res.status, 400);
197 - t.is(res.body.error, "Nenhum campo para atualizar");
198 - });
199 -
200 - test.serial("Deletar usuário existente", async (t) => {
201 - const res = await request(app).delete(`/users/${userId}`);
202 -
203 - t.is(res.status, 200);
204 - t.true(res.body.success);
205 - t.is(Number(res.body.deleted), Number(userId));
206 -
207 - // Verify user is deleted
208 - const user = db.prepare("SELECT * FROM users WHERE id = ?").get(userId);
209 - t.falsy(user);
210 - });
211 -
212 - test.serial("Deletar usuário deve deletar seus registros de humores (em cascata)", async (t) => {
213 - // Add a mood
214 - const now = new Date().toISOString();
215 - const moodResult = db.prepare(`
216 - INSERT INTO mood (user_id, rating, stress_level, anxiety_level, energy_level, recorded_at)
217 - VALUES (?, ?, ?, ?, ?, ?)
218 - `).run(userId, 7, 3, 4, 8, now);
219 -
220 - const moodId = moodResult.lastInsertRowid;
221 -
222 - // Add mood components
223 - db.prepare(`
224 - INSERT INTO mood_components (mood_id, emotion, intensity)
225 - VALUES (?, ?, ?)
226 - `).run(moodId, 'joy', 8);
227 -
228 - // Delete user
229 - const res = await request(app).delete(`/users/${userId}`);
230 - t.is(res.status, 200);
231 -
232 - // Verify moods are deleted
233 - const mood = db.prepare("SELECT * FROM mood WHERE user_id = ?").get(userId);
234 - t.falsy(mood);
235 -
236 - // Verify mood components are deleted
237 - const component = db.prepare("SELECT * FROM mood_components WHERE mood_id = ?").get(moodId);
238 - t.falsy(component);
239 - });
240 -
241 - test.serial("Falha ao deletar usuário inexistente", async (t) => {
242 - const res = await request(app).delete("/users/99999");
243 -
244 - t.is(res.status, 404);
245 - t.is(res.body.error, "Usuário não encontrado");
246 - });