diff --git a/config.json.example b/config.json.example
index 3875097d6c..9872fad88f 100644
--- a/config.json.example
+++ b/config.json.example
@@ -86,5 +86,6 @@
"RATE_LIMITER_ENABLED": "false",
"REDIS_HOST": "aaabbbcccdddeeefff",
"REDIS_PORT": "1234",
- "REDIS_PASSWORD": "12345678"
+ "REDIS_PASSWORD": "12345678",
+ "TRUSTED_DOMAINS": "https://localhost,https://habitica.com"
}
diff --git a/habitica-images b/habitica-images
index 7051701604..311cb01059 160000
--- a/habitica-images
+++ b/habitica-images
@@ -1 +1 @@
-Subproject commit 7051701604ff6ea246bf96995f4869ca95696d88
+Subproject commit 311cb010591a9de368a1ab5db7506d2d3efbb10b
diff --git a/migrations/users/pi-day.js b/migrations/users/pi-day.js
index f4af8ff143..dd3f7d4f3f 100644
--- a/migrations/users/pi-day.js
+++ b/migrations/users/pi-day.js
@@ -3,7 +3,7 @@ import { v4 as uuid } from 'uuid';
import { model as User } from '../../website/server/models/user';
-const MIGRATION_NAME = '20220314_pi_day';
+const MIGRATION_NAME = '20230314_pi_day';
const progressCount = 1000;
let count = 0;
@@ -54,7 +54,7 @@ async function updateUser (user) {
export default async function processUsers () {
const query = {
migration: { $ne: MIGRATION_NAME },
- 'auth.timestamps.loggedin': { $gt: new Date('2022-02-15') },
+ 'auth.timestamps.loggedin': { $gt: new Date('2023-02-15') },
};
const fields = {
diff --git a/package-lock.json b/package-lock.json
index e2b06a9914..e43131bc51 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,15 +1,15 @@
{
"name": "habitica",
- "version": "4.259.0",
+ "version": "4.269.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
"requires": {
- "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
},
@@ -27,20 +27,20 @@
"integrity": "sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ=="
},
"@babel/core": {
- "version": "7.20.12",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz",
- "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.4.tgz",
+ "integrity": "sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==",
"requires": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.7",
- "@babel/helper-compilation-targets": "^7.20.7",
- "@babel/helper-module-transforms": "^7.20.11",
- "@babel/helpers": "^7.20.7",
- "@babel/parser": "^7.20.7",
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
+ "@babel/helper-compilation-targets": "^7.21.4",
+ "@babel/helper-module-transforms": "^7.21.2",
+ "@babel/helpers": "^7.21.0",
+ "@babel/parser": "^7.21.4",
"@babel/template": "^7.20.7",
- "@babel/traverse": "^7.20.12",
- "@babel/types": "^7.20.7",
+ "@babel/traverse": "^7.21.4",
+ "@babel/types": "^7.21.4",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -49,44 +49,54 @@
},
"dependencies": {
"@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+ "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
"requires": {
"@babel/highlight": "^7.18.6"
}
},
"@babel/compat-data": {
- "version": "7.20.10",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz",
- "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg=="
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.4.tgz",
+ "integrity": "sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g=="
},
"@babel/generator": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
- "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+ "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
"requires": {
- "@babel/types": "^7.20.7",
+ "@babel/types": "^7.21.4",
"@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
"@babel/helper-compilation-targets": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz",
- "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz",
+ "integrity": "sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==",
"requires": {
- "@babel/compat-data": "^7.20.5",
- "@babel/helper-validator-option": "^7.18.6",
+ "@babel/compat-data": "^7.21.4",
+ "@babel/helper-validator-option": "^7.21.0",
"browserslist": "^4.21.3",
"lru-cache": "^5.1.1",
"semver": "^6.3.0"
}
},
+ "@babel/helper-function-name": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+ "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "requires": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ }
+ },
"@babel/helper-module-transforms": {
- "version": "7.20.11",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz",
- "integrity": "sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==",
+ "version": "7.21.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz",
+ "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==",
"requires": {
"@babel/helper-environment-visitor": "^7.18.9",
"@babel/helper-module-imports": "^7.18.6",
@@ -94,8 +104,8 @@
"@babel/helper-split-export-declaration": "^7.18.6",
"@babel/helper-validator-identifier": "^7.19.1",
"@babel/template": "^7.20.7",
- "@babel/traverse": "^7.20.10",
- "@babel/types": "^7.20.7"
+ "@babel/traverse": "^7.21.2",
+ "@babel/types": "^7.21.2"
}
},
"@babel/helper-validator-identifier": {
@@ -103,6 +113,11 @@
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
},
+ "@babel/helper-validator-option": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
+ "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ=="
+ },
"@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
@@ -114,9 +129,9 @@
}
},
"@babel/parser": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz",
- "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg=="
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw=="
},
"@babel/template": {
"version": "7.20.7",
@@ -129,57 +144,50 @@
}
},
"@babel/traverse": {
- "version": "7.20.12",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz",
- "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
"requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.7",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
"@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-function-name": "^7.21.0",
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+ "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
"requires": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
"to-fast-properties": "^2.0.0"
}
},
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
},
- "browserslist": {
- "version": "4.21.4",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
- "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
- "requires": {
- "caniuse-lite": "^1.0.30001400",
- "electron-to-chromium": "^1.4.251",
- "node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.9"
- }
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
- "caniuse-lite": {
- "version": "1.0.30001442",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz",
- "integrity": "sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow=="
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
+ "requires": {
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
+ }
},
"chalk": {
"version": "2.4.2",
@@ -191,11 +199,6 @@
"supports-color": "^5.3.0"
}
},
- "json5": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
- "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
- },
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -209,15 +212,6 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
- "update-browserslist-db": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
- "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
- "requires": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
- }
- },
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -666,33 +660,43 @@
}
},
"@babel/helpers": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz",
- "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==",
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz",
+ "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==",
"requires": {
"@babel/template": "^7.20.7",
- "@babel/traverse": "^7.20.7",
- "@babel/types": "^7.20.7"
+ "@babel/traverse": "^7.21.0",
+ "@babel/types": "^7.21.0"
},
"dependencies": {
"@babel/code-frame": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
- "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
+ "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
"requires": {
"@babel/highlight": "^7.18.6"
}
},
"@babel/generator": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
- "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.4.tgz",
+ "integrity": "sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==",
"requires": {
- "@babel/types": "^7.20.7",
+ "@babel/types": "^7.21.4",
"@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
}
},
+ "@babel/helper-function-name": {
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
+ "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "requires": {
+ "@babel/template": "^7.20.7",
+ "@babel/types": "^7.21.0"
+ }
+ },
"@babel/helper-validator-identifier": {
"version": "7.19.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
@@ -709,9 +713,9 @@
}
},
"@babel/parser": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz",
- "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg=="
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.4.tgz",
+ "integrity": "sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw=="
},
"@babel/template": {
"version": "7.20.7",
@@ -724,40 +728,49 @@
}
},
"@babel/traverse": {
- "version": "7.20.12",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.12.tgz",
- "integrity": "sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.4.tgz",
+ "integrity": "sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==",
"requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.20.7",
+ "@babel/code-frame": "^7.21.4",
+ "@babel/generator": "^7.21.4",
"@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.19.0",
+ "@babel/helper-function-name": "^7.21.0",
"@babel/helper-hoist-variables": "^7.18.6",
"@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.20.7",
- "@babel/types": "^7.20.7",
+ "@babel/parser": "^7.21.4",
+ "@babel/types": "^7.21.4",
"debug": "^4.1.0",
"globals": "^11.1.0"
}
},
"@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "version": "7.21.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.4.tgz",
+ "integrity": "sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==",
"requires": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
"to-fast-properties": "^2.0.0"
}
},
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.18",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
+ "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
"requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
}
},
"chalk": {
@@ -1487,9 +1500,9 @@
}
},
"@babel/register": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.18.9.tgz",
- "integrity": "sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw==",
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.21.0.tgz",
+ "integrity": "sha512-9nKsPmYDi5DidAqJaQooxIhsLJiNMkGr8ypQ8Uic7cIox7UCDsM7HuUGxdGT7mSDTYbqzIdsOWzfBton/YJrMw==",
"requires": {
"clone-deep": "^4.0.1",
"find-cache-dir": "^2.0.0",
@@ -1667,15 +1680,135 @@
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
},
"@esbuild/android-arm": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.12.tgz",
- "integrity": "sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA==",
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
+ "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==",
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz",
+ "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==",
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.17.tgz",
+ "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==",
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz",
+ "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==",
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz",
+ "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==",
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz",
+ "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==",
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz",
+ "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==",
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz",
+ "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==",
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz",
+ "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==",
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz",
+ "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==",
"optional": true
},
"@esbuild/linux-loong64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz",
- "integrity": "sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw==",
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz",
+ "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==",
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz",
+ "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==",
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz",
+ "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==",
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz",
+ "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==",
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz",
+ "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==",
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz",
+ "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==",
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz",
+ "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==",
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz",
+ "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==",
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz",
+ "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==",
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz",
+ "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==",
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz",
+ "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==",
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz",
+ "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==",
"optional": true
},
"@google-cloud/common": {
@@ -1743,12 +1876,13 @@
}
},
"@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
"requires": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
}
},
"@jridgewell/resolve-uri": {
@@ -2033,9 +2167,9 @@
"integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw=="
},
"@sinonjs/commons": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
- "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
+ "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
"dev": true,
"requires": {
"type-detect": "4.0.8"
@@ -2048,17 +2182,39 @@
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0"
+ },
+ "dependencies": {
+ "@sinonjs/commons": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+ "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ }
}
},
"@sinonjs/samsam": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz",
- "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==",
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz",
+ "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==",
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0",
"lodash.get": "^4.4.2",
"type-detect": "^4.0.8"
+ },
+ "dependencies": {
+ "@sinonjs/commons": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+ "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
+ "dev": true,
+ "requires": {
+ "type-detect": "4.0.8"
+ }
+ }
}
},
"@sinonjs/text-encoding": {
@@ -2147,9 +2303,9 @@
}
},
"@types/eslint": {
- "version": "8.4.7",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.7.tgz",
- "integrity": "sha512-ehM7cCt2RSFs42mb+lcmhFT9ouIlV92PuaeRGn8N8c98oMjG4Z5pJHA9b1QiCcuqnbPSHcyfiD3mlhqMaHsQIw==",
+ "version": "8.21.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.21.0.tgz",
+ "integrity": "sha512-35EhHNOXgxnUgh4XCJsGhE7zdlDhYDN/aMG6UbkByCFFNgQ7b3U+uVoqBpicFydR8JEfgdjCF7SJ7MiJfzuiTA==",
"requires": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -2636,9 +2792,9 @@
}
},
"apidoc": {
- "version": "0.53.1",
- "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.53.1.tgz",
- "integrity": "sha512-ijiLtIVEzTMdF29B/QzkvR4weMatgcElMsYKP1asszrImWYwzlZ9x0ZMLTXZrCe7GVMtkGSwQdugdLTZMZ+lww==",
+ "version": "0.54.0",
+ "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.54.0.tgz",
+ "integrity": "sha512-VCOdwkAaFK7bDLbiAiFKqX8SVlAnk7sQCXDARwshBQpVRqRGDNY993kAMPkWmSD1RCVBDMFIOgOfOR9mcISNZQ==",
"requires": {
"bootstrap": "3.4.1",
"commander": "^8.3.0",
@@ -2663,9 +2819,9 @@
},
"dependencies": {
"anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"requires": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -2829,13 +2985,13 @@
}
},
"apple-auth": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/apple-auth/-/apple-auth-1.0.7.tgz",
- "integrity": "sha512-vfJqy4KtT5KHflxBSemc0mkWuy2GU2wHWaZ6xVWUjPmiXkJcLj5jB3IeCbDLF4wN+ZStjOQXZWfGwccBjclVlA==",
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/apple-auth/-/apple-auth-1.0.9.tgz",
+ "integrity": "sha512-nm3h/sXpwA6kyBWa1l8Sl3c8nusIQMnUklBL8eTUDb++LpF2k2GZ66OaDj5lba7Arw9JpHfuzewBF92mh9lNPQ==",
"requires": {
"axios": "^0.21.1",
"express": "^4.17.1",
- "jsonwebtoken": "^8.5.1"
+ "jsonwebtoken": "^9.0.0"
},
"dependencies": {
"axios": {
@@ -2845,6 +3001,44 @@
"requires": {
"follow-redirects": "^1.14.0"
}
+ },
+ "jsonwebtoken": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
+ "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
+ "requires": {
+ "jws": "^3.2.2",
+ "lodash": "^4.17.21",
+ "ms": "^2.1.1",
+ "semver": "^7.3.8"
+ }
+ },
+ "jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "requires": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "requires": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
}
}
},
@@ -3152,9 +3346,9 @@
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
},
"axios": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz",
- "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==",
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
+ "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
"dev": true,
"requires": {
"follow-redirects": "^1.15.0",
@@ -3922,12 +4116,12 @@
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"body-parser": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
- "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
+ "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"requires": {
"bytes": "3.1.2",
- "content-type": "~1.0.4",
+ "content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@@ -3935,11 +4129,16 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
- "raw-body": "2.5.1",
+ "raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"dependencies": {
+ "content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
+ },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -3976,6 +4175,17 @@
"requires": {
"side-channel": "^1.0.4"
}
+ },
+ "raw-body": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
}
}
},
@@ -4075,20 +4285,25 @@
"dev": true
},
"browserslist": {
- "version": "4.21.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
- "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
+ "version": "4.21.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+ "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
"requires": {
- "caniuse-lite": "^1.0.30001366",
- "electron-to-chromium": "^1.4.188",
- "node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.4"
+ "caniuse-lite": "^1.0.30001449",
+ "electron-to-chromium": "^1.4.284",
+ "node-releases": "^2.0.8",
+ "update-browserslist-db": "^1.0.10"
},
"dependencies": {
"electron-to-chromium": {
- "version": "1.4.192",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.192.tgz",
- "integrity": "sha512-8nCXyIQY9An88NXAp+PuPy5h3/w5ZY7Iu2lag65Q0XREprcat5F8gKhoHsBUnQcFuCRnmevpR8yEBYRU3d2HDw=="
+ "version": "1.4.295",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz",
+ "integrity": "sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw=="
+ },
+ "node-releases": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+ "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w=="
}
}
},
@@ -4246,9 +4461,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001367",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
- "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw=="
+ "version": "1.0.30001451",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz",
+ "integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w=="
},
"caseless": {
"version": "0.12.0",
@@ -4879,9 +5094,9 @@
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"cookiejar": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz",
- "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ=="
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz",
+ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw=="
},
"cookies": {
"version": "0.8.0",
@@ -5759,9 +5974,9 @@
}
},
"enhanced-resolve": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
- "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz",
+ "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==",
"requires": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -5871,167 +6086,47 @@
}
},
"esbuild": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.12.tgz",
- "integrity": "sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng==",
+ "version": "0.16.17",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.17.tgz",
+ "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==",
"requires": {
- "@esbuild/android-arm": "0.15.12",
- "@esbuild/linux-loong64": "0.15.12",
- "esbuild-android-64": "0.15.12",
- "esbuild-android-arm64": "0.15.12",
- "esbuild-darwin-64": "0.15.12",
- "esbuild-darwin-arm64": "0.15.12",
- "esbuild-freebsd-64": "0.15.12",
- "esbuild-freebsd-arm64": "0.15.12",
- "esbuild-linux-32": "0.15.12",
- "esbuild-linux-64": "0.15.12",
- "esbuild-linux-arm": "0.15.12",
- "esbuild-linux-arm64": "0.15.12",
- "esbuild-linux-mips64le": "0.15.12",
- "esbuild-linux-ppc64le": "0.15.12",
- "esbuild-linux-riscv64": "0.15.12",
- "esbuild-linux-s390x": "0.15.12",
- "esbuild-netbsd-64": "0.15.12",
- "esbuild-openbsd-64": "0.15.12",
- "esbuild-sunos-64": "0.15.12",
- "esbuild-windows-32": "0.15.12",
- "esbuild-windows-64": "0.15.12",
- "esbuild-windows-arm64": "0.15.12"
+ "@esbuild/android-arm": "0.16.17",
+ "@esbuild/android-arm64": "0.16.17",
+ "@esbuild/android-x64": "0.16.17",
+ "@esbuild/darwin-arm64": "0.16.17",
+ "@esbuild/darwin-x64": "0.16.17",
+ "@esbuild/freebsd-arm64": "0.16.17",
+ "@esbuild/freebsd-x64": "0.16.17",
+ "@esbuild/linux-arm": "0.16.17",
+ "@esbuild/linux-arm64": "0.16.17",
+ "@esbuild/linux-ia32": "0.16.17",
+ "@esbuild/linux-loong64": "0.16.17",
+ "@esbuild/linux-mips64el": "0.16.17",
+ "@esbuild/linux-ppc64": "0.16.17",
+ "@esbuild/linux-riscv64": "0.16.17",
+ "@esbuild/linux-s390x": "0.16.17",
+ "@esbuild/linux-x64": "0.16.17",
+ "@esbuild/netbsd-x64": "0.16.17",
+ "@esbuild/openbsd-x64": "0.16.17",
+ "@esbuild/sunos-x64": "0.16.17",
+ "@esbuild/win32-arm64": "0.16.17",
+ "@esbuild/win32-ia32": "0.16.17",
+ "@esbuild/win32-x64": "0.16.17"
}
},
- "esbuild-android-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz",
- "integrity": "sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q==",
- "optional": true
- },
- "esbuild-android-arm64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz",
- "integrity": "sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA==",
- "optional": true
- },
- "esbuild-darwin-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz",
- "integrity": "sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q==",
- "optional": true
- },
- "esbuild-darwin-arm64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz",
- "integrity": "sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw==",
- "optional": true
- },
- "esbuild-freebsd-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz",
- "integrity": "sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw==",
- "optional": true
- },
- "esbuild-freebsd-arm64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz",
- "integrity": "sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g==",
- "optional": true
- },
- "esbuild-linux-32": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz",
- "integrity": "sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA==",
- "optional": true
- },
- "esbuild-linux-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz",
- "integrity": "sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA==",
- "optional": true
- },
- "esbuild-linux-arm": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz",
- "integrity": "sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A==",
- "optional": true
- },
- "esbuild-linux-arm64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz",
- "integrity": "sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ==",
- "optional": true
- },
- "esbuild-linux-mips64le": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz",
- "integrity": "sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A==",
- "optional": true
- },
- "esbuild-linux-ppc64le": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz",
- "integrity": "sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg==",
- "optional": true
- },
- "esbuild-linux-riscv64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz",
- "integrity": "sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA==",
- "optional": true
- },
- "esbuild-linux-s390x": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz",
- "integrity": "sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww==",
- "optional": true
- },
"esbuild-loader": {
- "version": "2.20.0",
- "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz",
- "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==",
+ "version": "2.21.0",
+ "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.21.0.tgz",
+ "integrity": "sha512-k7ijTkCT43YBSZ6+fBCW1Gin7s46RrJ0VQaM8qA7lq7W+OLsGgtLyFV8470FzYi/4TeDexniTBTPTwZUnXXR5g==",
"requires": {
- "esbuild": "^0.15.6",
+ "esbuild": "^0.16.17",
"joycon": "^3.0.1",
"json5": "^2.2.0",
"loader-utils": "^2.0.0",
"tapable": "^2.2.0",
- "webpack-sources": "^2.2.0"
+ "webpack-sources": "^1.4.3"
}
},
- "esbuild-netbsd-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz",
- "integrity": "sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w==",
- "optional": true
- },
- "esbuild-openbsd-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz",
- "integrity": "sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw==",
- "optional": true
- },
- "esbuild-sunos-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz",
- "integrity": "sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg==",
- "optional": true
- },
- "esbuild-windows-32": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz",
- "integrity": "sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw==",
- "optional": true
- },
- "esbuild-windows-64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz",
- "integrity": "sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA==",
- "optional": true
- },
- "esbuild-windows-arm64": {
- "version": "0.15.12",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz",
- "integrity": "sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA==",
- "optional": true
- },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -7729,9 +7824,9 @@
"dev": true
},
"glob": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
- "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -7749,9 +7844,9 @@
}
},
"minimatch": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
- "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"requires": {
"brace-expansion": "^2.0.1"
}
@@ -9704,9 +9799,9 @@
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="
},
"jquery": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
- "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw=="
+ "version": "3.6.3",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz",
+ "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg=="
},
"js-tokens": {
"version": "4.0.0",
@@ -9784,9 +9879,9 @@
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"json5": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz",
- "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ=="
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
},
"jsonfile": {
"version": "6.1.0",
@@ -11396,36 +11491,25 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
},
"nise": {
- "version": "5.1.3",
- "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.3.tgz",
- "integrity": "sha512-U597iWTTBBYIV72986jyU382/MMZ70ApWcRmkoF1AZ75bpqOtI3Gugv/6+0jLgoDOabmcSwYBkSSAWIp1eA5cg==",
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz",
+ "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==",
"dev": true,
"requires": {
"@sinonjs/commons": "^2.0.0",
- "@sinonjs/fake-timers": "^7.0.4",
+ "@sinonjs/fake-timers": "^10.0.2",
"@sinonjs/text-encoding": "^0.7.1",
"just-extend": "^4.0.2",
"path-to-regexp": "^1.7.0"
},
"dependencies": {
- "@sinonjs/fake-timers": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz",
- "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==",
+ "@sinonjs/commons": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz",
+ "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==",
"dev": true,
"requires": {
- "@sinonjs/commons": "^1.7.0"
- },
- "dependencies": {
- "@sinonjs/commons": {
- "version": "1.8.6",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz",
- "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.8"
- }
- }
+ "type-detect": "4.0.8"
}
},
"isarray": {
@@ -13451,9 +13535,9 @@
}
},
"serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
+ "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
"requires": {
"randombytes": "^2.1.0"
}
@@ -13612,9 +13696,9 @@
}
},
"simple-update-notifier": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
- "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
+ "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
"requires": {
"semver": "~7.0.0"
},
@@ -13627,16 +13711,16 @@
}
},
"sinon": {
- "version": "15.0.1",
- "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz",
- "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==",
+ "version": "15.0.4",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.4.tgz",
+ "integrity": "sha512-uzmfN6zx3GQaria1kwgWGeKiXSSbShBbue6Dcj0SI8fiCNFbiUDqKl57WFlY5lyhxZVUKmXvzgG2pilRQCBwWg==",
"dev": true,
"requires": {
- "@sinonjs/commons": "^2.0.0",
- "@sinonjs/fake-timers": "10.0.2",
- "@sinonjs/samsam": "^7.0.1",
- "diff": "^5.0.0",
- "nise": "^5.1.2",
+ "@sinonjs/commons": "^3.0.0",
+ "@sinonjs/fake-timers": "^10.0.2",
+ "@sinonjs/samsam": "^8.0.0",
+ "diff": "^5.1.0",
+ "nise": "^5.1.4",
"supports-color": "^7.2.0"
},
"dependencies": {
@@ -14286,9 +14370,9 @@
}
},
"stripe": {
- "version": "11.6.0",
- "resolved": "https://registry.npmjs.org/stripe/-/stripe-11.6.0.tgz",
- "integrity": "sha512-ht8S1l8CJJE3jtv2NM1mEQzZBkITYvb9uDpSeXYeNz9iJkFFgDU169htwOW00OdIESvFaIsGgWQatAE5dfOERQ==",
+ "version": "11.10.0",
+ "resolved": "https://registry.npmjs.org/stripe/-/stripe-11.10.0.tgz",
+ "integrity": "sha512-uzQuj/Vangpp8fgkaEr6oRNfPToo5xamOhc7wxFGF/RUm7VKm/IGzoZ0cCtnGHCs5/O1AjMQsTZApgUARRVHOw==",
"requires": {
"@types/node": ">=8.1.0",
"qs": "^6.11.0"
@@ -14600,9 +14684,9 @@
"integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw=="
},
"terser": {
- "version": "5.15.1",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
- "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
+ "version": "5.16.3",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz",
+ "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==",
"requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -14611,9 +14695,9 @@
},
"dependencies": {
"acorn": {
- "version": "8.8.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
- "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w=="
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
},
"commander": {
"version": "2.20.3",
@@ -14992,9 +15076,9 @@
},
"dependencies": {
"json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
"requires": {
"minimist": "^1.2.0"
}
@@ -15310,9 +15394,9 @@
"integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="
},
"update-browserslist-db": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz",
- "integrity": "sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"requires": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
@@ -15514,9 +15598,9 @@
}
},
"validator": {
- "version": "13.7.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
- "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz",
+ "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA=="
},
"value-or-function": {
"version": "3.0.0",
@@ -15782,9 +15866,9 @@
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
},
"webpack": {
- "version": "5.74.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
- "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
+ "version": "5.75.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz",
+ "integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==",
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
@@ -15813,9 +15897,9 @@
},
"dependencies": {
"acorn": {
- "version": "8.8.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
- "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w=="
+ "version": "8.8.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+ "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
},
"eslint-scope": {
"version": "5.1.1",
@@ -15902,12 +15986,12 @@
}
},
"webpack-sources": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz",
- "integrity": "sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==",
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
"requires": {
- "source-list-map": "^2.0.1",
- "source-map": "^0.6.1"
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
},
"dependencies": {
"source-map": {
@@ -16149,9 +16233,9 @@
}
},
"xml2js": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
- "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
+ "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
diff --git a/package.json b/package.json
index f5c9da55c7..fd55d4a746 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,22 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
- "version": "4.259.0",
+ "version": "4.269.0",
"main": "./website/server/index.js",
"dependencies": {
- "@babel/core": "^7.20.12",
+ "@babel/core": "^7.21.4",
"@babel/preset-env": "^7.20.2",
- "@babel/register": "^7.18.9",
+ "@babel/register": "^7.21.0",
"@google-cloud/trace-agent": "^7.1.2",
"@parse/node-apn": "^5.1.3",
"@slack/webhook": "^6.1.0",
"accepts": "^1.3.8",
"amazon-payments": "^0.2.9",
"amplitude": "^6.0.0",
- "apidoc": "^0.53.1",
- "apple-auth": "^1.0.7",
+ "apidoc": "^0.54.0",
+ "apple-auth": "^1.0.9",
"bcrypt": "^5.1.0",
- "body-parser": "^1.20.1",
+ "body-parser": "^1.20.2",
"bootstrap": "^4.6.0",
"compression": "^1.7.4",
"cookie-session": "^2.0.0",
@@ -30,7 +30,7 @@
"express": "^4.18.2",
"express-basic-auth": "^1.2.1",
"express-validator": "^5.2.0",
- "glob": "^8.0.3",
+ "glob": "^8.1.0",
"got": "^11.8.3",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
@@ -67,16 +67,16 @@
"remove-markdown": "^0.5.0",
"rimraf": "^3.0.2",
"short-uuid": "^4.2.2",
- "stripe": "^11.6.0",
+ "stripe": "^11.10.0",
"superagent": "^8.0.6",
"universal-analytics": "^0.5.3",
"useragent": "^2.1.9",
"uuid": "^9.0.0",
- "validator": "^13.7.0",
+ "validator": "^13.9.0",
"vinyl-buffer": "^1.0.1",
"winston": "^3.8.2",
"winston-loggly-bulk": "^3.2.1",
- "xml2js": "^0.4.23"
+ "xml2js": "^0.5.0"
},
"private": true,
"engines": {
@@ -110,7 +110,7 @@
"apidoc": "gulp apidoc"
},
"devDependencies": {
- "axios": "^1.2.2",
+ "axios": "^1.3.6",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"chai-moment": "^0.1.0",
@@ -122,7 +122,7 @@
"monk": "^7.3.4",
"require-again": "^2.0.0",
"run-rs": "^0.7.7",
- "sinon": "^15.0.1",
+ "sinon": "^15.0.4",
"sinon-chai": "^3.7.0",
"sinon-stub-promise": "^4.0.0"
},
diff --git a/test/api/unit/libs/cron.test.js b/test/api/unit/libs/cron.test.js
index eb15119ee1..6757228033 100644
--- a/test/api/unit/libs/cron.test.js
+++ b/test/api/unit/libs/cron.test.js
@@ -231,13 +231,16 @@ describe('cron', async () => {
},
});
// user1 has a 1-month recurring subscription starting today
- user1.purchased.plan.customerId = 'subscribedId';
- user1.purchased.plan.dateUpdated = moment().toDate();
- user1.purchased.plan.planId = 'basic';
- user1.purchased.plan.consecutive.count = 0;
- user1.purchased.plan.consecutive.offset = 0;
- user1.purchased.plan.consecutive.trinkets = 0;
- user1.purchased.plan.consecutive.gemCapExtra = 0;
+ beforeEach(async () => {
+ user1.purchased.plan.customerId = 'subscribedId';
+ user1.purchased.plan.dateUpdated = moment().toDate();
+ user1.purchased.plan.planId = 'basic';
+ user1.purchased.plan.consecutive.count = 0;
+ user1.purchased.plan.perkMonthCount = 0;
+ user1.purchased.plan.consecutive.offset = 0;
+ user1.purchased.plan.consecutive.trinkets = 0;
+ user1.purchased.plan.consecutive.gemCapExtra = 0;
+ });
it('does not increment consecutive benefits after the first month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -271,6 +274,24 @@ describe('cron', async () => {
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(0);
});
+ it('increments consecutive benefits after the second month if they also received a 1 month gift subscription', async () => {
+ user1.purchased.plan.perkMonthCount = 1;
+ clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
+ .add(2, 'days')
+ .toDate());
+ // Add 1 month to simulate what happens a month after the subscription was created.
+ // Add 2 days so that we're sure we're not affected by any start-of-month effects
+ // e.g., from time zone oddness.
+ await cron({
+ user: user1, tasksByType, daysMissed, analytics,
+ });
+ expect(user1.purchased.plan.perkMonthCount).to.equal(0);
+ expect(user1.purchased.plan.consecutive.count).to.equal(2);
+ expect(user1.purchased.plan.consecutive.offset).to.equal(0);
+ expect(user1.purchased.plan.consecutive.trinkets).to.equal(1);
+ expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(5);
+ });
+
it('increments consecutive benefits after the third month', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(3, 'months')
.add(2, 'days')
@@ -315,6 +336,30 @@ describe('cron', async () => {
expect(user1.purchased.plan.consecutive.trinkets).to.equal(3);
expect(user1.purchased.plan.consecutive.gemCapExtra).to.equal(15);
});
+
+ it('initializes plan.perkMonthCount if necessary', async () => {
+ user.purchased.plan.perkMonthCount = undefined;
+ clock = sinon.useFakeTimers(moment(user.purchased.plan.dateUpdated)
+ .utcOffset(0)
+ .startOf('month')
+ .add(1, 'months')
+ .add(2, 'days')
+ .toDate());
+ await cron({
+ user, tasksByType, daysMissed, analytics,
+ });
+ expect(user.purchased.plan.perkMonthCount).to.equal(1);
+ user.purchased.plan.perkMonthCount = undefined;
+ user.purchased.plan.consecutive.count = 8;
+ clock.restore();
+ clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(2, 'months')
+ .add(2, 'days')
+ .toDate());
+ await cron({
+ user, tasksByType, daysMissed, analytics,
+ });
+ expect(user.purchased.plan.perkMonthCount).to.equal(2);
+ });
});
describe('for a 3-month recurring subscription', async () => {
@@ -330,13 +375,16 @@ describe('cron', async () => {
},
});
// user3 has a 3-month recurring subscription starting today
- user3.purchased.plan.customerId = 'subscribedId';
- user3.purchased.plan.dateUpdated = moment().toDate();
- user3.purchased.plan.planId = 'basic_3mo';
- user3.purchased.plan.consecutive.count = 0;
- user3.purchased.plan.consecutive.offset = 3;
- user3.purchased.plan.consecutive.trinkets = 1;
- user3.purchased.plan.consecutive.gemCapExtra = 5;
+ beforeEach(async () => {
+ user3.purchased.plan.customerId = 'subscribedId';
+ user3.purchased.plan.dateUpdated = moment().toDate();
+ user3.purchased.plan.planId = 'basic_3mo';
+ user3.purchased.plan.perkMonthCount = 0;
+ user3.purchased.plan.consecutive.count = 0;
+ user3.purchased.plan.consecutive.offset = 3;
+ user3.purchased.plan.consecutive.trinkets = 1;
+ user3.purchased.plan.consecutive.gemCapExtra = 5;
+ });
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -390,6 +438,21 @@ describe('cron', async () => {
expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
});
+ it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
+ user3.purchased.plan.perkMonthCount = 2;
+ user3.purchased.plan.consecutive.trinkets = 1;
+ user3.purchased.plan.consecutive.gemCapExtra = 5;
+ clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(4, 'months')
+ .add(2, 'days')
+ .toDate());
+ await cron({
+ user: user3, tasksByType, daysMissed, analytics,
+ });
+ expect(user3.purchased.plan.perkMonthCount).to.equal(2);
+ expect(user3.purchased.plan.consecutive.trinkets).to.equal(2);
+ expect(user3.purchased.plan.consecutive.gemCapExtra).to.equal(10);
+ });
+
it('does not increment consecutive benefits in the second month of the second period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(5, 'months')
.add(2, 'days')
@@ -456,13 +519,16 @@ describe('cron', async () => {
},
});
// user6 has a 6-month recurring subscription starting today
- user6.purchased.plan.customerId = 'subscribedId';
- user6.purchased.plan.dateUpdated = moment().toDate();
- user6.purchased.plan.planId = 'google_6mo';
- user6.purchased.plan.consecutive.count = 0;
- user6.purchased.plan.consecutive.offset = 6;
- user6.purchased.plan.consecutive.trinkets = 2;
- user6.purchased.plan.consecutive.gemCapExtra = 10;
+ beforeEach(async () => {
+ user6.purchased.plan.customerId = 'subscribedId';
+ user6.purchased.plan.dateUpdated = moment().toDate();
+ user6.purchased.plan.planId = 'google_6mo';
+ user6.purchased.plan.perkMonthCount = 0;
+ user6.purchased.plan.consecutive.count = 0;
+ user6.purchased.plan.consecutive.offset = 6;
+ user6.purchased.plan.consecutive.trinkets = 2;
+ user6.purchased.plan.consecutive.gemCapExtra = 10;
+ });
it('does not increment consecutive benefits in the first month of the first paid period that they already have benefits for', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(1, 'months')
@@ -503,6 +569,19 @@ describe('cron', async () => {
expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
});
+ it('keeps existing plan.perkMonthCount intact when incrementing consecutive benefits', async () => {
+ user6.purchased.plan.perkMonthCount = 2;
+ clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(7, 'months')
+ .add(2, 'days')
+ .toDate());
+ await cron({
+ user: user6, tasksByType, daysMissed, analytics,
+ });
+ expect(user6.purchased.plan.perkMonthCount).to.equal(2);
+ expect(user6.purchased.plan.consecutive.trinkets).to.equal(4);
+ expect(user6.purchased.plan.consecutive.gemCapExtra).to.equal(20);
+ });
+
it('increments consecutive benefits the month after the third paid period has started', async () => {
clock = sinon.useFakeTimers(moment().utcOffset(0).startOf('month').add(13, 'months')
.add(2, 'days')
diff --git a/test/api/unit/libs/payments/apple.test.js b/test/api/unit/libs/payments/apple.test.js
index 217a307d0b..2f01fef793 100644
--- a/test/api/unit/libs/payments/apple.test.js
+++ b/test/api/unit/libs/payments/apple.test.js
@@ -29,8 +29,9 @@ describe('Apple Payments', () => {
.resolves();
iapValidateStub = sinon.stub(iap, 'validate')
.resolves({});
- iapIsValidatedStub = sinon.stub(iap, 'isValidated')
- .returns(true);
+ iapIsValidatedStub = sinon.stub(iap, 'isValidated').returns(true);
+ sinon.stub(iap, 'isExpired').returns(false);
+ sinon.stub(iap, 'isCanceled').returns(false);
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
productId: 'com.habitrpg.ios.Habitica.21gems',
@@ -44,6 +45,8 @@ describe('Apple Payments', () => {
iap.setup.restore();
iap.validate.restore();
iap.isValidated.restore();
+ iap.isExpired.restore();
+ iap.isCanceled.restore();
iap.getPurchaseData.restore();
payments.buySkuItem.restore();
gems.validateGiftMessage.restore();
@@ -218,6 +221,7 @@ describe('Apple Payments', () => {
headers = {};
receipt = `{"token": "${token}"}`;
nextPaymentProcessing = moment.utc().add({ days: 2 });
+ user = new User();
iapSetupStub = sinon.stub(iap, 'setup')
.resolves();
@@ -228,14 +232,17 @@ describe('Apple Payments', () => {
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
expirationDate: moment.utc().subtract({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().valueOf(),
productId: sku,
transactionId: token,
}, {
expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().valueOf(),
productId: 'wrongsku',
transactionId: token,
}, {
expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().valueOf(),
productId: sku,
transactionId: token,
}]);
@@ -250,21 +257,12 @@ describe('Apple Payments', () => {
if (payments.createSubscription.restore) payments.createSubscription.restore();
});
- it('should throw an error if sku is empty', async () => {
- await expect(applePayments.subscribe('', user, receipt, headers, nextPaymentProcessing))
- .to.eventually.be.rejected.and.to.eql({
- httpCode: 400,
- name: 'BadRequest',
- message: i18n.t('missingSubscriptionCode'),
- });
- });
-
it('should throw an error if receipt is invalid', async () => {
iap.isValidated.restore();
iapIsValidatedStub = sinon.stub(iap, 'isValidated')
.returns(false);
- await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
+ await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
.to.eventually.be.rejected.and.to.eql({
httpCode: 401,
name: 'NotAuthorized',
@@ -295,13 +293,15 @@ describe('Apple Payments', () => {
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{
- expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ expirationDate: moment.utc().add({ day: 2 }).toDate(),
+ purchaseDate: new Date(),
productId: option.sku,
transactionId: token,
+ originalTransactionId: token,
}]);
sub = common.content.subscriptionBlocks[option.subKey];
- await applePayments.subscribe(option.sku, user, receipt, headers, nextPaymentProcessing);
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
expect(iapSetupStub).to.be.calledOnce;
expect(iapValidateStub).to.be.calledOnce;
@@ -321,21 +321,253 @@ describe('Apple Payments', () => {
nextPaymentProcessing,
});
});
+ if (option !== subOptions[3]) {
+ const newOption = subOptions[3];
+ it(`upgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
+ const oldSub = common.content.subscriptionBlocks[option.subKey];
+ oldSub.logic = 'refundAndRepay';
+ user.profile.name = 'sender';
+ user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
+ user.purchased.plan.customerId = token;
+ user.purchased.plan.planId = option.subKey;
+ user.purchased.plan.additionalData = receipt;
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 2 }).toDate(),
+ purchaseDate: moment.utc().valueOf(),
+ productId: newOption.sku,
+ transactionId: `${token}new`,
+ originalTransactionId: token,
+ }]);
+ sub = common.content.subscriptionBlocks[newOption.subKey];
+
+ await applePayments.subscribe(user,
+ receipt,
+ headers,
+ nextPaymentProcessing);
+
+ expect(iapSetupStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
+ expect(iapIsValidatedStub).to.be.calledOnce;
+ expect(iapIsValidatedStub).to.be.calledWith({});
+ expect(iapGetPurchaseDataStub).to.be.calledOnce;
+
+ expect(paymentsCreateSubscritionStub).to.be.calledOnce;
+ expect(paymentsCreateSubscritionStub).to.be.calledWith({
+ user,
+ customerId: token,
+ paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
+ sub,
+ headers,
+ additionalData: receipt,
+ nextPaymentProcessing,
+ updatedFrom: oldSub,
+ });
+ });
+ }
+ if (option !== subOptions[0]) {
+ const newOption = subOptions[0];
+ it(`downgrades a subscription from ${option.sku} to ${newOption.sku}`, async () => {
+ const oldSub = common.content.subscriptionBlocks[option.subKey];
+ user.profile.name = 'sender';
+ user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
+ user.purchased.plan.customerId = token;
+ user.purchased.plan.planId = option.subKey;
+ user.purchased.plan.additionalData = receipt;
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 2 }).toDate(),
+ purchaseDate: moment.utc().valueOf(),
+ productId: newOption.sku,
+ transactionId: `${token}new`,
+ originalTransactionId: token,
+ }]);
+ sub = common.content.subscriptionBlocks[newOption.subKey];
+
+ await applePayments.subscribe(user,
+ receipt,
+ headers,
+ nextPaymentProcessing);
+
+ expect(iapSetupStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
+ expect(iapIsValidatedStub).to.be.calledOnce;
+ expect(iapIsValidatedStub).to.be.calledWith({});
+ expect(iapGetPurchaseDataStub).to.be.calledOnce;
+
+ expect(paymentsCreateSubscritionStub).to.be.calledOnce;
+ expect(paymentsCreateSubscritionStub).to.be.calledWith({
+ user,
+ customerId: token,
+ paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
+ sub,
+ headers,
+ additionalData: receipt,
+ nextPaymentProcessing,
+ updatedFrom: oldSub,
+ });
+ });
+ }
});
- it('errors when a user is already subscribed', async () => {
- payments.createSubscription.restore();
- user = new User();
- await user.save();
+ it('uses the most recent subscription data', async () => {
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 4 }).toDate(),
+ purchaseDate: moment.utc().subtract({ day: 5 }).toDate(),
+ productId: 'com.habitrpg.ios.habitica.subscription.3month',
+ transactionId: `${token}oldest`,
+ originalTransactionId: `${token}evenOlder`,
+ }, {
+ expirationDate: moment.utc().add({ day: 2 }).toDate(),
+ purchaseDate: moment.utc().subtract({ day: 1 }).toDate(),
+ productId: 'com.habitrpg.ios.habitica.subscription.12month',
+ transactionId: `${token}newest`,
+ originalTransactionId: `${token}newest`,
+ }, {
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().subtract({ day: 2 }).toDate(),
+ productId: 'com.habitrpg.ios.habitica.subscription.6month',
+ transactionId: token,
+ originalTransactionId: token,
+ }]);
+ sub = common.content.subscriptionBlocks.basic_12mo;
- await applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing);
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
- await expect(applePayments.subscribe(sku, user, receipt, headers, nextPaymentProcessing))
- .to.eventually.be.rejected.and.to.eql({
- httpCode: 401,
- name: 'NotAuthorized',
- message: applePayments.constants.RESPONSE_ALREADY_USED,
- });
+ expect(paymentsCreateSubscritionStub).to.be.calledOnce;
+ expect(paymentsCreateSubscritionStub).to.be.calledWith({
+ user,
+ customerId: `${token}newest`,
+ paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
+ sub,
+ headers,
+ additionalData: receipt,
+ nextPaymentProcessing,
+ });
+ });
+
+ describe('does not apply multiple times', async () => {
+ it('errors when a user is using the same subscription', async () => {
+ payments.createSubscription.restore();
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().toDate(),
+ productId: sku,
+ transactionId: token,
+ originalTransactionId: token,
+ }]);
+
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
+
+ await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
+ .to.eventually.be.rejected.and.to.eql({
+ httpCode: 401,
+ name: 'NotAuthorized',
+ message: applePayments.constants.RESPONSE_ALREADY_USED,
+ });
+ });
+
+ it('errors when a user is using a rebill of the same subscription', async () => {
+ user = new User();
+ await user.save();
+ payments.createSubscription.restore();
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().toDate(),
+ productId: sku,
+ transactionId: `${token}renew`,
+ originalTransactionId: token,
+ }]);
+
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
+
+ await expect(applePayments.subscribe(user, receipt, headers, nextPaymentProcessing))
+ .to.eventually.be.rejected.and.to.eql({
+ httpCode: 401,
+ name: 'NotAuthorized',
+ message: applePayments.constants.RESPONSE_ALREADY_USED,
+ });
+ });
+
+ it('errors when a different user is using the subscription', async () => {
+ user = new User();
+ await user.save();
+ payments.createSubscription.restore();
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().toDate(),
+ productId: sku,
+ transactionId: token,
+ originalTransactionId: token,
+ }]);
+
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
+
+ const secondUser = new User();
+ await secondUser.save();
+ await expect(applePayments.subscribe(
+ secondUser, receipt, headers, nextPaymentProcessing,
+ ))
+ .to.eventually.be.rejected.and.to.eql({
+ httpCode: 401,
+ name: 'NotAuthorized',
+ message: applePayments.constants.RESPONSE_ALREADY_USED,
+ });
+ });
+
+ it('errors when a multiple users exist using the subscription', async () => {
+ user = new User();
+ await user.save();
+ payments.createSubscription.restore();
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().toDate(),
+ productId: sku,
+ transactionId: token,
+ originalTransactionId: token,
+ }]);
+
+ await applePayments.subscribe(user, receipt, headers, nextPaymentProcessing);
+ const secondUser = new User();
+ secondUser.purchased.plan = user.purchased.plan;
+ secondUser.purchased.plan.dateTerminate = new Date();
+ secondUser.save();
+
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{
+ expirationDate: moment.utc().add({ day: 1 }).toDate(),
+ purchaseDate: moment.utc().toDate(),
+ productId: sku,
+ transactionId: `${token}new`,
+ originalTransactionId: token,
+ }]);
+
+ const thirdUser = new User();
+ await thirdUser.save();
+ await expect(applePayments.subscribe(
+ thirdUser, receipt, headers, nextPaymentProcessing,
+ ))
+ .to.eventually.be.rejected.and.to.eql({
+ httpCode: 401,
+ name: 'NotAuthorized',
+ message: applePayments.constants.RESPONSE_ALREADY_USED,
+ });
+ });
});
});
@@ -360,9 +592,9 @@ describe('Apple Payments', () => {
});
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{ expirationDate: expirationDate.toDate() }]);
- iapIsValidatedStub = sinon.stub(iap, 'isValidated')
- .returns(true);
-
+ iapIsValidatedStub = sinon.stub(iap, 'isValidated').returns(true);
+ sinon.stub(iap, 'isCanceled').returns(false);
+ sinon.stub(iap, 'isExpired').returns(true);
user = new User();
user.profile.name = 'sender';
user.purchased.plan.paymentMethod = applePayments.constants.PAYMENT_METHOD_APPLE;
@@ -377,6 +609,8 @@ describe('Apple Payments', () => {
iap.setup.restore();
iap.validate.restore();
iap.isValidated.restore();
+ iap.isExpired.restore();
+ iap.isCanceled.restore();
iap.getPurchaseData.restore();
payments.cancelSubscription.restore();
});
@@ -396,6 +630,8 @@ describe('Apple Payments', () => {
iap.getPurchaseData.restore();
iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
.returns([{ expirationDate: expirationDate.add({ day: 1 }).toDate() }]);
+ iap.isExpired.restore();
+ sinon.stub(iap, 'isExpired').returns(false);
await expect(applePayments.cancelSubscribe(user, headers))
.to.eventually.be.rejected.and.to.eql({
@@ -418,7 +654,38 @@ describe('Apple Payments', () => {
});
});
- it('should cancel a user subscription', async () => {
+ it('should cancel a cancelled subscription with termination date in the future', async () => {
+ const futureDate = expirationDate.add({ day: 1 });
+ iap.getPurchaseData.restore();
+ iapGetPurchaseDataStub = sinon.stub(iap, 'getPurchaseData')
+ .returns([{ expirationDate: futureDate }]);
+ iap.isExpired.restore();
+ sinon.stub(iap, 'isExpired').returns(false);
+
+ iap.isCanceled.restore();
+ sinon.stub(iap, 'isCanceled').returns(true);
+
+ await applePayments.cancelSubscribe(user, headers);
+
+ expect(iapSetupStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledOnce;
+ expect(iapValidateStub).to.be.calledWith(iap.APPLE, receipt);
+ expect(iapIsValidatedStub).to.be.calledOnce;
+ expect(iapIsValidatedStub).to.be.calledWith({
+ expirationDate: futureDate,
+ });
+ expect(iapGetPurchaseDataStub).to.be.calledOnce;
+
+ expect(paymentCancelSubscriptionSpy).to.be.calledOnce;
+ expect(paymentCancelSubscriptionSpy).to.be.calledWith({
+ user,
+ paymentMethod: applePayments.constants.PAYMENT_METHOD_APPLE,
+ nextBill: futureDate.toDate(),
+ headers,
+ });
+ });
+
+ it('should cancel an expired subscription', async () => {
await applePayments.cancelSubscribe(user, headers);
expect(iapSetupStub).to.be.calledOnce;
diff --git a/test/api/unit/libs/payments/payments.test.js b/test/api/unit/libs/payments/payments.test.js
index 65b42b42f3..697a13fe1b 100644
--- a/test/api/unit/libs/payments/payments.test.js
+++ b/test/api/unit/libs/payments/payments.test.js
@@ -203,6 +203,28 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.dateCreated).to.exist;
});
+ it('sets plan.dateCurrentTypeCreated if it did not previously exist', async () => {
+ expect(recipient.purchased.plan.dateCurrentTypeCreated).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.dateCurrentTypeCreated).to.exist;
+ });
+
+ it('keeps plan.dateCreated when changing subscription type', async () => {
+ await api.createSubscription(data);
+ const initialDate = recipient.purchased.plan.dateCreated;
+ await api.createSubscription(data);
+ expect(recipient.purchased.plan.dateCreated).to.eql(initialDate);
+ });
+
+ it('sets plan.dateCurrentTypeCreated when changing subscription type', async () => {
+ await api.createSubscription(data);
+ const initialDate = recipient.purchased.plan.dateCurrentTypeCreated;
+ await api.createSubscription(data);
+ expect(recipient.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate);
+ });
+
it('does not change plan.customerId if it already exists', async () => {
recipient.purchased.plan = plan;
data.customerId = 'purchaserCustomerId';
@@ -213,6 +235,116 @@ describe('payments/index', () => {
expect(recipient.purchased.plan.customerId).to.eql('customer-id');
});
+ it('sets plan.perkMonthCount to 1 if user is not subscribed', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = 1;
+ recipient.purchased.plan.customerId = undefined;
+ data.sub.key = 'basic_earned';
+ data.gift.subscription.key = 'basic_earned';
+ data.gift.subscription.months = 1;
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
+ });
+
+ it('sets plan.perkMonthCount to 1 if field is not initialized', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = -1;
+ recipient.purchased.plan.customerId = undefined;
+ data.sub.key = 'basic_earned';
+ data.gift.subscription.key = 'basic_earned';
+ data.gift.subscription.months = 1;
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
+ });
+
+ it('sets plan.perkMonthCount to 1 if user had previous count but lapsed subscription', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = 2;
+ recipient.purchased.plan.customerId = undefined;
+ data.sub.key = 'basic_earned';
+ data.gift.subscription.key = 'basic_earned';
+ data.gift.subscription.months = 1;
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
+ });
+
+ it('adds to plan.perkMonthCount if user is already subscribed', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = 1;
+ data.sub.key = 'basic_earned';
+ data.gift.subscription.key = 'basic_earned';
+ data.gift.subscription.months = 1;
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(1);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
+ });
+
+ it('awards perks if plan.perkMonthCount reaches 3 with existing subscription', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = 2;
+ data.sub.key = 'basic_earned';
+ data.gift.subscription.key = 'basic_earned';
+ data.gift.subscription.months = 1;
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+ });
+
+ it('awards perks if plan.perkMonthCount reaches 3 without existing subscription', async () => {
+ recipient.purchased.plan.perkMonthCount = 0;
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+ });
+
+ it('awards perks if plan.perkMonthCount reaches 3 without initialized field', async () => {
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(-1);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+ });
+
+ it('awards perks if plan.perkMonthCount goes over 3', async () => {
+ recipient.purchased.plan = plan;
+ recipient.purchased.plan.perkMonthCount = 2;
+ data.sub.key = 'basic_earned';
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(0);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+ await api.createSubscription(data);
+
+ expect(recipient.purchased.plan.perkMonthCount).to.eql(2);
+ expect(recipient.purchased.plan.consecutive.trinkets).to.eql(1);
+ expect(recipient.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+ });
+
it('sets plan.customerId to "Gift" if it does not already exist', async () => {
expect(recipient.purchased.plan.customerId).to.not.exist;
@@ -379,6 +511,7 @@ describe('payments/index', () => {
expect(user.purchased.plan.customerId).to.eql('customer-id');
expect(user.purchased.plan.dateUpdated).to.exist;
expect(user.purchased.plan.gemsBought).to.eql(0);
+ expect(user.purchased.plan.perkMonthCount).to.eql(0);
expect(user.purchased.plan.paymentMethod).to.eql('Payment Method');
expect(user.purchased.plan.extraMonths).to.eql(0);
expect(user.purchased.plan.dateTerminated).to.eql(null);
@@ -386,6 +519,63 @@ describe('payments/index', () => {
expect(user.purchased.plan.dateCreated).to.exist;
});
+ it('sets plan.dateCreated if it did not previously exist', async () => {
+ expect(user.purchased.plan.dateCreated).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.dateCreated).to.exist;
+ });
+
+ it('sets plan.dateCurrentTypeCreated if it did not previously exist', async () => {
+ expect(user.purchased.plan.dateCurrentTypeCreated).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.dateCurrentTypeCreated).to.exist;
+ });
+
+ it('keeps plan.dateCreated when changing subscription type', async () => {
+ await api.createSubscription(data);
+ const initialDate = user.purchased.plan.dateCreated;
+ await api.createSubscription(data);
+ expect(user.purchased.plan.dateCreated).to.eql(initialDate);
+ });
+
+ it('sets plan.dateCurrentTypeCreated when changing subscription type', async () => {
+ await api.createSubscription(data);
+ const initialDate = user.purchased.plan.dateCurrentTypeCreated;
+ await api.createSubscription(data);
+ expect(user.purchased.plan.dateCurrentTypeCreated).to.not.eql(initialDate);
+ });
+
+ it('keeps plan.perkMonthCount when changing subscription type', async () => {
+ await api.createSubscription(data);
+ user.purchased.plan.perkMonthCount = 2;
+ await api.createSubscription(data);
+ expect(user.purchased.plan.perkMonthCount).to.eql(2);
+ });
+
+ it('sets plan.perkMonthCount to zero when creating new monthly subscription', async () => {
+ user.purchased.plan.perkMonthCount = 2;
+ await api.createSubscription(data);
+ expect(user.purchased.plan.perkMonthCount).to.eql(0);
+ });
+
+ it('sets plan.perkMonthCount to zero when creating new 3 month subscription', async () => {
+ user.purchased.plan.perkMonthCount = 2;
+ await api.createSubscription(data);
+ expect(user.purchased.plan.perkMonthCount).to.eql(0);
+ });
+
+ it('updates plan.consecutive.offset when changing subscription type', async () => {
+ await api.createSubscription(data);
+ expect(user.purchased.plan.consecutive.offset).to.eql(3);
+ data.sub.key = 'basic_6mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.consecutive.offset).to.eql(6);
+ });
+
it('awards the Royal Purple Jackalope pet', async () => {
await api.createSubscription(data);
@@ -465,6 +655,89 @@ describe('payments/index', () => {
},
});
});
+
+ context('Upgrades subscription', () => {
+ it('from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ const created = user.purchased.plan.dateCreated;
+ const updated = user.purchased.plan.dateUpdated;
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom = { key: 'basic_earned' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.dateCreated).to.eql(created);
+ expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ });
+
+ it('from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ const created = user.purchased.plan.dateCreated;
+ const updated = user.purchased.plan.dateUpdated;
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom = { key: 'basic_3mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.dateCreated).to.eql(created);
+ expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ });
+ });
+
+ context('Downgrades subscription', () => {
+ it('from basic_6mo to basic_earned', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ const created = user.purchased.plan.dateCreated;
+ const updated = user.purchased.plan.dateUpdated;
+
+ data.sub.key = 'basic_earned';
+ data.updatedFrom = { key: 'basic_6mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.dateCreated).to.eql(created);
+ expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ });
+
+ it('from basic_12mo to basic_3mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ data.sub.key = 'basic_12mo';
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ const created = user.purchased.plan.dateCreated;
+ const updated = user.purchased.plan.dateUpdated;
+
+ data.sub.key = 'basic_3mo';
+ data.updatedFrom = { key: 'basic_12mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.dateCreated).to.eql(created);
+ expect(user.purchased.plan.dateUpdated).to.not.eql(updated);
+ expect(user.purchased.plan.customerId).to.eql('customer-id');
+ });
+ });
});
context('Block subscription perks', () => {
@@ -488,7 +761,6 @@ describe('payments/index', () => {
it('adds 10 to plan.consecutive.gemCapExtra for 6 month block', async () => {
data.sub.key = 'basic_6mo';
-
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
@@ -496,7 +768,6 @@ describe('payments/index', () => {
it('adds 20 to plan.consecutive.gemCapExtra for 12 month block', async () => {
data.sub.key = 'basic_12mo';
-
await api.createSubscription(data);
expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
@@ -532,6 +803,532 @@ describe('payments/index', () => {
expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
});
+
+ context('Upgrades subscription', () => {
+ context('Using payDifference logic', () => {
+ beforeEach(async () => {
+ data.updatedFrom = { logic: 'payDifference' };
+ });
+ it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+ });
+
+ it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+
+ it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+ });
+
+ context('Using payFull logic', () => {
+ beforeEach(async () => {
+ data.updatedFrom = { logic: 'payFull' };
+ });
+ it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+ });
+
+ it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
+ });
+ });
+
+ context('Using refundAndRepay logic', () => {
+ let clock;
+ beforeEach(async () => {
+ clock = sinon.useFakeTimers(new Date('2022-01-01'));
+ data.updatedFrom = { logic: 'refundAndRepay' };
+ });
+ context('Upgrades within first half of subscription', () => {
+ it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-10'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+ });
+
+ it('Adds 15 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-02-05'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-08'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-31'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2024-01-08'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-08-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+
+ it('Adds 3 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-07-31'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+ });
+ context('Upgrades within second half of subscription', () => {
+ it('Adds 10 to plan.consecutive.gemCapExtra from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-20'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+ });
+
+ it('Adds 20 to plan.consecutive.gemCapExtra when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(5);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-02-24'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(25);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-01-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-05-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-03-03'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
+ });
+
+ it('Adds 2 to plan.consecutive.trinkets from basic_earned to basic_6mo after initial cycle', async () => {
+ data.sub.key = 'basic_earned';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
+
+ data.sub.key = 'basic_6mo';
+ data.updatedFrom.key = 'basic_earned';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2022-05-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_6mo to basic_12mo after initial cycle', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_6mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2023-05-28'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(6);
+ });
+
+ it('Adds 4 to plan.consecutive.trinkets when upgrading from basic_3mo to basic_12mo after initial cycle', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_3mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
+
+ data.sub.key = 'basic_12mo';
+ data.updatedFrom.key = 'basic_3mo';
+ clock.restore();
+ clock = sinon.useFakeTimers(new Date('2023-09-03'));
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(5);
+ });
+ });
+ afterEach(async () => {
+ if (clock !== null) clock.restore();
+ });
+ });
+ });
+
+ context('Downgrades subscription', () => {
+ it('does not remove from plan.consecutive.gemCapExtra from basic_6mo to basic_earned', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+
+ data.sub.key = 'basic_earned';
+ data.updatedFrom = { key: 'basic_6mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(10);
+ });
+
+ it('does not remove from plan.consecutive.gemCapExtra from basic_12mo to basic_3mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ data.sub.key = 'basic_12mo';
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
+
+ data.sub.key = 'basic_3mo';
+ data.updatedFrom = { key: 'basic_12mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.consecutive.gemCapExtra).to.eql(20);
+ });
+
+ it('does not remove from plan.consecutive.trinkets from basic_6mo to basic_earned', async () => {
+ data.sub.key = 'basic_6mo';
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_6mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+
+ data.sub.key = 'basic_earned';
+ data.updatedFrom = { key: 'basic_6mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.planId).to.eql('basic_earned');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(2);
+ });
+
+ it('does not remove from plan.consecutive.trinkets from basic_12mo to basic_3mo', async () => {
+ expect(user.purchased.plan.planId).to.not.exist;
+
+ data.sub.key = 'basic_12mo';
+ await api.createSubscription(data);
+
+ expect(user.purchased.plan.planId).to.eql('basic_12mo');
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+
+ data.sub.key = 'basic_3mo';
+ data.updatedFrom = { key: 'basic_12mo' };
+ await api.createSubscription(data);
+ expect(user.purchased.plan.consecutive.trinkets).to.eql(4);
+ });
+ });
});
context('Mystery Items', () => {
diff --git a/test/api/unit/models/group.test.js b/test/api/unit/models/group.test.js
index fd5cea9b05..24cf64a250 100644
--- a/test/api/unit/models/group.test.js
+++ b/test/api/unit/models/group.test.js
@@ -1359,6 +1359,7 @@ describe('Group Model', () => {
describe('#sendChat', () => {
beforeEach(() => {
sandbox.spy(User, 'update');
+ sandbox.spy(User, 'updateMany');
});
it('formats message', () => {
@@ -1413,8 +1414,8 @@ describe('Group Model', () => {
it('updates users about new messages in party', () => {
party.sendChat({ message: 'message' });
- expect(User.update).to.be.calledOnce;
- expect(User.update).to.be.calledWithMatch({
+ expect(User.updateMany).to.be.calledOnce;
+ expect(User.updateMany).to.be.calledWithMatch({
'party._id': party._id,
_id: { $ne: '' },
});
@@ -1427,8 +1428,8 @@ describe('Group Model', () => {
group.sendChat({ message: 'message' });
- expect(User.update).to.be.calledOnce;
- expect(User.update).to.be.calledWithMatch({
+ expect(User.updateMany).to.be.calledOnce;
+ expect(User.updateMany).to.be.calledWithMatch({
guilds: group._id,
_id: { $ne: '' },
});
@@ -1437,8 +1438,8 @@ describe('Group Model', () => {
it('does not send update to user that sent the message', () => {
party.sendChat({ message: 'message', user: { _id: 'user-id', profile: { name: 'user' } } });
- expect(User.update).to.be.calledOnce;
- expect(User.update).to.be.calledWithMatch({
+ expect(User.updateMany).to.be.calledOnce;
+ expect(User.updateMany).to.be.calledWithMatch({
'party._id': party._id,
_id: { $ne: 'user-id' },
});
diff --git a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js
index 92acf9023f..bbad7a8401 100644
--- a/test/api/v3/integration/groups/POST-groups_groupId_join.test.js
+++ b/test/api/v3/integration/groups/POST-groups_groupId_join.test.js
@@ -258,47 +258,6 @@ describe('POST /group/:groupId/join', () => {
await expect(user.get('/user')).to.eventually.have.nested.property('items.quests.basilist', 2);
});
- it('deletes previous party where the user was the only member', async () => {
- const userToInvite = await generateUser();
- const oldParty = await userToInvite.post('/groups', { // add user to a party
- name: 'Another Test Party',
- type: 'party',
- });
-
- await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true);
- await user.post(`/groups/${party._id}/invite`, {
- uuids: [userToInvite._id],
- });
- await userToInvite.post(`/groups/${party._id}/join`);
-
- await expect(user.get('/user')).to.eventually.have.nested.property('party._id', party._id);
- await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(false);
- });
-
- it('does not allow user to leave a party if a quest was active and they were the only member', async () => {
- const userToInvite = await generateUser();
- const oldParty = await userToInvite.post('/groups', { // add user to a party
- name: 'Another Test Party',
- type: 'party',
- });
-
- await userToInvite.update({
- [`items.quests.${PET_QUEST}`]: 1,
- });
- await userToInvite.post(`/groups/${oldParty._id}/quests/invite/${PET_QUEST}`);
-
- await expect(checkExistence('groups', oldParty._id)).to.eventually.equal(true);
- await user.post(`/groups/${party._id}/invite`, {
- uuids: [userToInvite._id],
- });
-
- await expect(userToInvite.post(`/groups/${party._id}/join`)).to.eventually.be.rejected.and.eql({
- code: 401,
- error: 'NotAuthorized',
- message: t('messageCannotLeaveWhileQuesting'),
- });
- });
-
it('invites joining member to active quest', async () => {
await user.update({
[`items.quests.${PET_QUEST}`]: 1,
diff --git a/test/api/v3/integration/groups/POST-groups_invite.test.js b/test/api/v3/integration/groups/POST-groups_invite.test.js
index 02f8c5e20c..58a478be00 100644
--- a/test/api/v3/integration/groups/POST-groups_invite.test.js
+++ b/test/api/v3/integration/groups/POST-groups_invite.test.js
@@ -1,6 +1,7 @@
import { v4 as generateUUID } from 'uuid';
import nconf from 'nconf';
import {
+ createAndPopulateGroup,
generateUser,
generateGroup,
translate as t,
@@ -581,20 +582,7 @@ describe('Post /groups/:groupId/invite', () => {
});
});
- it('allow inviting a user to a party if they are partying solo', async () => {
- const userToInvite = await generateUser();
- await userToInvite.post('/groups', { // add user to a party
- name: 'Another Test Party',
- type: 'party',
- });
-
- await inviter.post(`/groups/${party._id}/invite`, {
- uuids: [userToInvite._id],
- });
- expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
- });
-
- it('allow inviting a user to 2 different parties', async () => {
+ it('allows inviting a user to 2 different parties', async () => {
// Create another inviter
const inviter2 = await generateUser();
@@ -635,29 +623,47 @@ describe('Post /groups/:groupId/invite', () => {
});
expect((await userToInvite.get('/user')).invitations.parties[0].id).to.equal(party._id);
});
+ });
+
+ describe('party size limits', () => {
+ let party, partyLeader;
+
+ beforeEach(async () => {
+ group = await createAndPopulateGroup({
+ groupDetails: {
+ name: 'Test Party',
+ type: 'party',
+ privacy: 'private',
+ },
+ // Generate party with 20 members
+ members: PARTY_LIMIT_MEMBERS - 10,
+ });
+ party = group.group;
+ partyLeader = group.groupLeader;
+ });
it('allows 30 members in a party', async () => {
const invitesToGenerate = [];
- // Generate 29 users to invite (29 + leader = 30 members)
- for (let i = 0; i < PARTY_LIMIT_MEMBERS - 1; i += 1) {
+ // Generate 10 new invites
+ for (let i = 1; i < 10; i += 1) {
invitesToGenerate.push(generateUser());
}
const generatedInvites = await Promise.all(invitesToGenerate);
// Invite users
- expect(await inviter.post(`/groups/${party._id}/invite`, {
+ expect(await partyLeader.post(`/groups/${party._id}/invite`, {
uuids: generatedInvites.map(invite => invite._id),
})).to.be.an('array');
}).timeout(10000);
- it('does not allow 30+ members in a party', async () => {
+ it('does not allow >30 members in a party', async () => {
const invitesToGenerate = [];
- // Generate 30 users to invite (30 + leader = 31 members)
- for (let i = 0; i < PARTY_LIMIT_MEMBERS; i += 1) {
+ // Generate 11 invites
+ for (let i = 1; i < 11; i += 1) {
invitesToGenerate.push(generateUser());
}
const generatedInvites = await Promise.all(invitesToGenerate);
// Invite users
- await expect(inviter.post(`/groups/${party._id}/invite`, {
+ await expect(partyLeader.post(`/groups/${party._id}/invite`, {
uuids: generatedInvites.map(invite => invite._id),
}))
.to.eventually.be.rejected.and.eql({
diff --git a/test/api/v3/integration/payments/apple/POST-payments_apple_subscribe.test.js b/test/api/v3/integration/payments/apple/POST-payments_apple_subscribe.test.js
index faf5f0e6ea..5a9aa456e7 100644
--- a/test/api/v3/integration/payments/apple/POST-payments_apple_subscribe.test.js
+++ b/test/api/v3/integration/payments/apple/POST-payments_apple_subscribe.test.js
@@ -45,11 +45,10 @@ describe('payments : apple #subscribe', () => {
});
expect(subscribeStub).to.be.calledOnce;
- expect(subscribeStub.args[0][0]).to.eql(sku);
- expect(subscribeStub.args[0][1]._id).to.eql(user._id);
- expect(subscribeStub.args[0][2]).to.eql('receipt');
- expect(subscribeStub.args[0][3]['x-api-key']).to.eql(user.apiToken);
- expect(subscribeStub.args[0][3]['x-api-user']).to.eql(user._id);
+ expect(subscribeStub.args[0][0]._id).to.eql(user._id);
+ expect(subscribeStub.args[0][1]).to.eql('receipt');
+ expect(subscribeStub.args[0][2]['x-api-key']).to.eql(user.apiToken);
+ expect(subscribeStub.args[0][2]['x-api-user']).to.eql(user._id);
});
});
});
diff --git a/test/api/v3/integration/world-state/GET-world-state.test.js b/test/api/v3/integration/world-state/GET-world-state.test.js
index ef2f9aca46..cec7086d6f 100644
--- a/test/api/v3/integration/world-state/GET-world-state.test.js
+++ b/test/api/v3/integration/world-state/GET-world-state.test.js
@@ -35,13 +35,6 @@ describe('GET /world-state', () => {
});
});
- it('returns a string representing the current season for NPC sprites', async () => {
- const res = await requester().get('/world-state');
-
- expect(res).to.have.nested.property('npcImageSuffix');
- expect(res.npcImageSuffix).to.be.a('string');
- });
-
context('no current event', () => {
beforeEach(async () => {
sinon.stub(worldState, 'getCurrentEvent').returns(null);
diff --git a/test/common/libs/cron.test.js b/test/common/libs/cron.test.js
index d082e1e7b0..e8be92fceb 100644
--- a/test/common/libs/cron.test.js
+++ b/test/common/libs/cron.test.js
@@ -215,6 +215,7 @@ describe('cron utility functions', () => {
it('monthly plan, next date in 3 months', () => {
const user = baseUserData(60, 0, 'group_plan_auto');
+ user.purchased.plan.perkMonthCount = 0;
const planContext = getPlanContext(user, now);
@@ -224,6 +225,7 @@ describe('cron utility functions', () => {
it('monthly plan, next date in 1 month', () => {
const user = baseUserData(62, 0, 'group_plan_auto');
+ user.purchased.plan.perkMonthCount = 2;
const planContext = getPlanContext(user, now);
@@ -248,5 +250,15 @@ describe('cron utility functions', () => {
expect(planContext.nextHourglassDate)
.to.be.sameMoment('2022-07-10T02:00:00.144Z');
});
+
+ it('multi-month plan with perk count', () => {
+ const user = baseUserData(60, 1, 'basic_3mo');
+ user.purchased.plan.perkMonthCount = 2;
+
+ const planContext = getPlanContext(user, now);
+
+ expect(planContext.nextHourglassDate)
+ .to.be.sameMoment('2022-07-10T02:00:00.144Z');
+ });
});
});
diff --git a/test/helpers/api-integration/external-server.js b/test/helpers/api-integration/external-server.js
index 5b199d058d..5f02eb03c3 100644
--- a/test/helpers/api-integration/external-server.js
+++ b/test/helpers/api-integration/external-server.js
@@ -12,8 +12,9 @@ const webhookData = {};
app.use(bodyParser.urlencoded({
extended: true,
+ limit: '10mb',
}));
-app.use(bodyParser.json());
+app.use(bodyParser.json({ limit: '10mb' }));
app.post('/webhooks/:id', (req, res) => {
const { id } = req.params;
diff --git a/test/helpers/api-integration/requester.js b/test/helpers/api-integration/requester.js
index ad01f35978..61c0b2fabf 100644
--- a/test/helpers/api-integration/requester.js
+++ b/test/helpers/api-integration/requester.js
@@ -53,7 +53,8 @@ function _requestMaker (user, method, additionalSets = {}) {
if (user && user._id && user.apiToken) {
request
.set('x-api-user', user._id)
- .set('x-api-key', user.apiToken);
+ .set('x-api-key', user.apiToken)
+ .set('x-client', 'habitica-web');
}
if (!isEmpty(additionalSets)) {
diff --git a/website/client/package-lock.json b/website/client/package-lock.json
index 0af1f10a19..8cc351c0ea 100644
--- a/website/client/package-lock.json
+++ b/website/client/package-lock.json
@@ -1842,9 +1842,9 @@
}
},
"@babel/plugin-proposal-optional-chaining": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz",
- "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==",
+ "version": "7.21.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
+ "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
"requires": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
@@ -1870,9 +1870,9 @@
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
},
"@babel/types": {
- "version": "7.20.7",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
- "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
+ "version": "7.21.2",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz",
+ "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==",
"requires": {
"@babel/helper-string-parser": "^7.19.4",
"@babel/helper-validator-identifier": "^7.19.1",
@@ -4979,9 +4979,9 @@
}
},
"@sideway/formula": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz",
- "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg=="
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
+ "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg=="
},
"@sideway/pinpoint": {
"version": "2.0.0",
@@ -13318,11 +13318,31 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+ },
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
+ "loader-utils": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
@@ -13354,6 +13374,35 @@
"ansi-regex": "^5.0.1"
}
},
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "vue-loader-v16": {
+ "version": "npm:vue-loader@16.8.3",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
+ "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
+ "requires": {
+ "chalk": "^4.1.0",
+ "hash-sum": "^2.0.0",
+ "loader-utils": "^2.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ }
+ }
+ },
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -16852,9 +16901,9 @@
}
},
"core-js": {
- "version": "3.27.2",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.27.2.tgz",
- "integrity": "sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w=="
+ "version": "3.30.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz",
+ "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ=="
},
"core-js-compat": {
"version": "3.11.0",
@@ -17903,9 +17952,9 @@
}
},
"dompurify": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz",
- "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA=="
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.3.tgz",
+ "integrity": "sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ=="
},
"domutils": {
"version": "1.7.0",
@@ -20652,9 +20701,9 @@
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"hellojs": {
- "version": "1.19.5",
- "resolved": "https://registry.npmjs.org/hellojs/-/hellojs-1.19.5.tgz",
- "integrity": "sha512-hFlublej5rHFWjGe6MMMmj78otSIXBbrfvtWPZSSRmXKFoLMFlL41PJ1JPS8xwiSIZca2ve8uHoPZUDwU+/8gg=="
+ "version": "1.20.0",
+ "resolved": "https://registry.npmjs.org/hellojs/-/hellojs-1.20.0.tgz",
+ "integrity": "sha512-zfZGRt0J0OpJHnw2GJz4uh+rzMNAMWpIymiaRbSmCqCvqDMLSx2/qR5JucqnHPsZHUEjyKj5aLJ8K54kyHHjLQ=="
},
"hex-color-regex": {
"version": "1.1.0",
@@ -21964,9 +22013,9 @@
}
},
"jquery": {
- "version": "3.6.3",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.3.tgz",
- "integrity": "sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg=="
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
+ "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
},
"js-message": {
"version": "1.0.5",
@@ -27746,9 +27795,9 @@
}
},
"smartbanner.js": {
- "version": "1.19.1",
- "resolved": "https://registry.npmjs.org/smartbanner.js/-/smartbanner.js-1.19.1.tgz",
- "integrity": "sha512-x3alFTlk6pLuqrm9PrYQv1E+86CrEIgPf/KJ+nP5342BmOWstbdR8OwD3TPmM56zHQm4MEr/eoqbEcfTKdvdKw=="
+ "version": "1.19.2",
+ "resolved": "https://registry.npmjs.org/smartbanner.js/-/smartbanner.js-1.19.2.tgz",
+ "integrity": "sha512-hwcGNp5Hza5PJHTmqP6H8q0XBYhloIQyJgdzv0ldz3HQSeEuKB2riVraQXdKuquE6ZU/0M0yubno53xJ/ZiQQg=="
},
"snapdragon": {
"version": "0.8.2",
@@ -28120,9 +28169,9 @@
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
},
"stopword": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/stopword/-/stopword-2.0.5.tgz",
- "integrity": "sha512-MgmxgmVs0Uo9G4mMqRc/QBXdPePZUnVNYqnNiv8QdCXTqHmGRw36mrjToCeNxhu/7Ifa7RxAtd/KR/GLZdnN9g=="
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/stopword/-/stopword-2.0.8.tgz",
+ "integrity": "sha512-btlEC2vEuhCuvshz99hSGsY8GzaP5qzDPQm56j6rR/R38p8xdsOXgU5a6tIgvU/4hcCta1Vlo/2FVXA9m0f8XA=="
},
"store2": {
"version": "2.10.0",
@@ -29770,9 +29819,9 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"ua-parser-js": {
- "version": "0.7.31",
- "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.31.tgz",
- "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ=="
+ "version": "0.7.33",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.33.tgz",
+ "integrity": "sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw=="
},
"uc.micro": {
"version": "1.0.6",
@@ -30240,9 +30289,9 @@
}
},
"validator": {
- "version": "13.7.0",
- "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz",
- "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw=="
+ "version": "13.9.0",
+ "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz",
+ "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA=="
},
"vary": {
"version": "1.1.2",
@@ -30574,76 +30623,6 @@
}
}
},
- "vue-loader-v16": {
- "version": "npm:vue-loader@16.8.3",
- "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
- "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
- "requires": {
- "chalk": "^4.1.0",
- "hash-sum": "^2.0.0",
- "loader-utils": "^2.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- }
- },
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "requires": {
- "color-name": "~1.1.4"
- }
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
- },
- "emojis-list": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
- "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
- },
- "loader-utils": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
- "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^2.1.2"
- }
- },
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
"vue-mugen-scroll": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/vue-mugen-scroll/-/vue-mugen-scroll-0.2.6.tgz",
diff --git a/website/client/package.json b/website/client/package.json
index 746bb82765..3ce61b456f 100644
--- a/website/client/package.json
+++ b/website/client/package.json
@@ -32,30 +32,30 @@
"bootstrap": "^4.6.0",
"bootstrap-vue": "^2.23.1",
"chai": "^4.3.7",
- "core-js": "^3.27.2",
- "dompurify": "^2.4.1",
+ "core-js": "^3.30.1",
+ "dompurify": "^2.4.3",
"eslint": "^6.8.0",
"eslint-config-habitrpg": "^6.2.0",
"eslint-plugin-mocha": "^5.3.0",
"eslint-plugin-vue": "^6.2.2",
"habitica-markdown": "^3.0.0",
- "hellojs": "^1.19.5",
+ "hellojs": "^1.20.0",
"inspectpack": "^4.7.1",
"intro.js": "^6.0.0",
- "jquery": "^3.6.3",
+ "jquery": "^3.6.4",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"nconf": "^0.12.0",
"sass": "^1.34.0",
"sass-loader": "^8.0.2",
- "smartbanner.js": "^1.19.1",
- "stopword": "^2.0.5",
+ "smartbanner.js": "^1.19.2",
+ "stopword": "^2.0.8",
"svg-inline-loader": "^0.8.2",
"svg-url-loader": "^7.1.1",
"svgo": "^1.3.2",
"svgo-loader": "^2.2.1",
"uuid": "^8.3.2",
- "validator": "^13.7.0",
+ "validator": "^13.9.0",
"vue": "^2.7.10",
"vue-cli-plugin-storybook": "2.1.0",
"vue-mugen-scroll": "^0.2.6",
@@ -66,6 +66,6 @@
"webpack": "^4.46.0"
},
"devDependencies": {
- "@babel/plugin-proposal-optional-chaining": "^7.20.7"
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0"
}
}
diff --git a/website/client/src/app.vue b/website/client/src/app.vue
index ede7eb8c9c..95cb2ba425 100644
--- a/website/client/src/app.vue
+++ b/website/client/src/app.vue
@@ -35,6 +35,7 @@
Contan que reside un terrible mal nas covas do Monte Habitica. Un monstro cuxa presenza ataca a vontade dos fortes heroes do lugar, guiándoos cara aos malos hábitos e a preguiza! A besta é un gran dragón de inmenso poder e composto das mesmas sombras: chámase Vicio, o traizoeiro Wyrm das Sombras. Valentes Habiticantes, levantádevos e derrotade esta besta repugnante dunha vez por todas, pero só se credes que podedes afrontar o seu inmenso poder.
Como podes esperar loitar contra a besta se xa pode controlarte? Non sexas vítima da preguiza e da adicción! Traballa duro para loitar contra a mala influencia do dragón e disipar o seu poder!
", + "questVice1Notes": "Contan que reside un terrible mal nas covas do Monte Habitica. Un monstro cuxa presenza ataca a vontade dos fortes heroes do lugar, guiándoos cara aos malos hábitos e a preguiza! A besta é un gran dragón de inmenso poder e composto das mesmas sombras: chámase Vicio, o traizoeiro Wyrm das Sombras. Valentes Habiticantes, levantádevos e derrotade esta besta repugnante dunha vez por todas, pero só se credes que podedes afrontar o seu inmenso poder.