diff --git a/.vscode/launch.json b/.vscode/launch.json index e86376b4b1..082837e935 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -740,6 +740,27 @@ "sourceMaps": true, "cwd": "${workspaceRoot}/services/sign/pod-sign" }, + { + "name": "Debug payment", + "type": "node", + "request": "launch", + "args": ["src/index.ts"], + "env": { + "PORT": "3040", + "SECRET": "secret", + "SERVICE_ID": "payment-service", + "ACCOUNTS_URL": "http://huly.local:3000", + "FRONT_URL": "http://huly.local:8087", + "USE_SANDBOX": "true", + // "POLAR_ACCESS_TOKEN": "polar_xxx", + // "POLAR_WEBHOOK_SECRET": "whsec_xxx", + // "POLAR_SUBSCRIPTION_PLANS": "common@tier:prod_1a,prod_1b;rare@tier:prod_2;epic@tier:prod_3;legendary@tier:prod_4" + }, + "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], + "runtimeVersion": "20", + "sourceMaps": true, + "cwd": "${workspaceRoot}/services/payment/pod-payment" + }, { "name": "Debug rekoni", "type": "node", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 2543527571..76ee989733 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -35,7 +35,7 @@ importers: specifier: ^8.4.1 version: 8.4.1 '@hcengineering/account-client': - specifier: ^0.7.17 + specifier: ^0.7.18 version: 0.7.18 '@hcengineering/analytics': specifier: ^0.7.17 @@ -193,6 +193,9 @@ importers: '@playwright/test': specifier: ^1.48.2 version: 1.56.1 + '@polar-sh/sdk': + specifier: ^0.37.0 + version: 0.37.0 '@rush-temp/account': specifier: file:./projects/account.tgz version: file:projects/account.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.11)(gcp-metadata@5.3.0(encoding@0.1.13))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(snappy@7.3.3)(socks@2.8.7)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) @@ -892,6 +895,9 @@ importers: '@rush-temp/panel': specifier: file:./projects/panel.tgz version: file:projects/panel.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@types/node@22.18.13)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.11)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) + '@rush-temp/payment-client': + specifier: file:./projects/payment-client.tgz + version: file:projects/payment-client.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) '@rush-temp/pod-account': specifier: file:./projects/pod-account.tgz version: file:projects/pod-account.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(encoding@0.1.13)(gcp-metadata@5.3.0(encoding@0.1.13))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(snappy@7.3.3)(socks@2.8.7) @@ -952,6 +958,9 @@ importers: '@rush-temp/pod-notification': specifier: file:./projects/pod-notification.tgz version: file:projects/pod-notification.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2) + '@rush-temp/pod-payment': + specifier: file:./projects/pod-payment.tgz + version: file:projects/pod-payment.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(bufferutil@4.0.9)(encoding@0.1.13)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(utf-8-validate@6.0.5) '@rush-temp/pod-preview': specifier: file:./projects/pod-preview.tgz version: file:projects/pod-preview.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(bufferutil@4.0.9)(encoding@0.1.13)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(utf-8-validate@6.0.5) @@ -1627,6 +1636,9 @@ importers: '@types/express-form-data': specifier: ^2.0.5 version: 2.0.5 + '@types/express-rate-limit': + specifier: ^6.0.0 + version: 6.0.2(express@4.21.2) '@types/heic-decode': specifier: ^1.1.2 version: 1.1.3 @@ -1921,6 +1933,9 @@ importers: express-form-data: specifier: ^2.0.22 version: 2.0.23 + express-rate-limit: + specifier: ^7.4.0 + version: 7.5.1(express@4.21.2) express-static-gzip: specifier: ^2.2.0 version: 2.2.0 @@ -4581,6 +4596,9 @@ packages: engines: {node: '>=18'} hasBin: true + '@polar-sh/sdk@0.37.0': + resolution: {integrity: sha512-sNAZjHurF3Teew+YKGudXqWvWvaSAkMAttdQUDDBvHfGm6KA7Dkyjoo83n8TmpTMuQVM5sycTUrrfPlPMNDXoQ==} + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -4629,1695 +4647,1703 @@ packages: resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} '@rush-temp/account-service@file:projects/account-service.tgz': - resolution: {integrity: sha512-Vd/InWXtZDBXFuvX+rEw3L8L9JTeK6uGUqEMLHH4CLu7g9fIEJdXnq4lDJqnrrn69WQ92CNRDSzuZLDVfOeCNg==, tarball: file:projects/account-service.tgz} + resolution: {integrity: sha512-8bEK6pusdRhHEuwNbgCEyB8jBUip93+lvcQz0CQEeY1W/oEW7ol1oRmyUYR0qr8KvcDF2+YfYIyu3DjIz+GFZA==, tarball: file:projects/account-service.tgz} version: 0.0.0 '@rush-temp/account@file:projects/account.tgz': - resolution: {integrity: sha512-olfeGvqP3IePmhOKT9c2dsodObO2uPLC9EMmEjko4dZsOiRktKRQqTGBe20KGzTUOZlduoj4abnAB7rrjdqEeg==, tarball: file:projects/account.tgz} + resolution: {integrity: sha512-S4RQoBeMAUVBzhM+iuLkwzd4D99vSogleibu4oCkpKkwmHFA7b8WwXK2rY73SHBkVooq8b6+e1NpCXAfFn/IKw==, tarball: file:projects/account.tgz} version: 0.0.0 '@rush-temp/achievement-assets@file:projects/achievement-assets.tgz': - resolution: {integrity: sha512-Fy7M/22+Y4OL5iafLHTqK8jpAnHGMdXyxNDg2cLqJFuYaDXvnT+r0uju6ZNGTyLzfTJqKklIELaMHTGYo1oEqg==, tarball: file:projects/achievement-assets.tgz} + resolution: {integrity: sha512-j6fX2tkHLYuNy6SIw9XxP8JnnjjEUDw0BcRunmwvRachj8WxEesjmvDVTN0VvVzrekZ74CDXaaBekSMJzW0EOg==, tarball: file:projects/achievement-assets.tgz} version: 0.0.0 '@rush-temp/achievement-resources@file:projects/achievement-resources.tgz': - resolution: {integrity: sha512-iC7VbLF4N5GRQNlCfoXPY0P9tPkf4mm3nyGOH1M9NZ2BtPfm+9wv/d15GWJp6XbL55sNrBMsbMtvk/32xAUqTw==, tarball: file:projects/achievement-resources.tgz} + resolution: {integrity: sha512-TrqIfO7PMCX8JG6vYYKC3psQZx2p/rMF0sYealvrEQyqRzsNaAxGFHOxX8f2SXL0HgtcJAXJiFpzQV2F2ou5KQ==, tarball: file:projects/achievement-resources.tgz} version: 0.0.0 '@rush-temp/achievement@file:projects/achievement.tgz': - resolution: {integrity: sha512-/xzrZmxeCpivoC3q24kexzDfUlC8LCGjRY9H2CZMa7dY9QuZR+7vmihza8aZbC/lriJY2B4e8tfPQKpqC/YRIw==, tarball: file:projects/achievement.tgz} + resolution: {integrity: sha512-1WjIYRLQ9xGsDUvieMQMk0cpDSiQzMcGRHaBaelR0FI25bKZHVWZRcfSZtwg0MeVsUCJzNrLTGiN7Ya+oenZqw==, tarball: file:projects/achievement.tgz} version: 0.0.0 '@rush-temp/activity-assets@file:projects/activity-assets.tgz': - resolution: {integrity: sha512-VW6nZlD0DrwJgLEeS7zE1ft9NSx2/Jexgtn0+HIKxgtnFo1EY7rFOO6igCZnRV34Doh4KmL5khMjgaay6gpEKA==, tarball: file:projects/activity-assets.tgz} + resolution: {integrity: sha512-TEF87d2AVCu5RvMFSbCCgOY0RgiLVRXAzuVtDrI+NU3fGm6oLifU8DKsm3ml+G+ONneZHM/LLDDga9+24wdj+Q==, tarball: file:projects/activity-assets.tgz} version: 0.0.0 '@rush-temp/activity-resources@file:projects/activity-resources.tgz': - resolution: {integrity: sha512-xJkpz6fjBPHFBJO+nv03Z/tb5WM5s4ww9ziKQ6Ixrb5MU9/ODDsZNu9Z8XaYxNfkpKv+5bg1c/tDz8+S8pFpxw==, tarball: file:projects/activity-resources.tgz} + resolution: {integrity: sha512-TqzVTM0yvEvT/RkBu4Cv80XTDiZ7W3gMcGYYxWUmuCitlozLu78B+7YKctASbHDM1QOZ/WBtMQV+T8oUgo0uFw==, tarball: file:projects/activity-resources.tgz} version: 0.0.0 '@rush-temp/activity@file:projects/activity.tgz': - resolution: {integrity: sha512-x9hGodEYWv+mVca5y/9pTJMR82U2CxpQAJ2KD6iRgNaHO7LMsM1zXXjlpPi81iiotAGjCoVUg92cOgIrwup5nw==, tarball: file:projects/activity.tgz} + resolution: {integrity: sha512-Q9QDQI8wZK6F5vHFopItessWWi4NOKhH33bsS+Bm/70PvbQlX1R7oSodCB1uIproH2SAMDo0BPRF71KCHVkitw==, tarball: file:projects/activity.tgz} version: 0.0.0 '@rush-temp/ai-assistant-assets@file:projects/ai-assistant-assets.tgz': - resolution: {integrity: sha512-nu5F/TuIZvFxTFWy9DEDEFNzuciUtNIz0jnhDuKuII5JPmaOM/hSVoAeZnFwZzfyuz4CrsyQ37L9Oadioq5vGg==, tarball: file:projects/ai-assistant-assets.tgz} + resolution: {integrity: sha512-5YQGiqAvJDNt7ypSNCbGc3gZHusAOu9ebCHTlFA6WorVOaULh7CpthEbayaa+nxk6IVEHvWYBsQTn3rBXWvI7w==, tarball: file:projects/ai-assistant-assets.tgz} version: 0.0.0 '@rush-temp/ai-assistant-resources@file:projects/ai-assistant-resources.tgz': - resolution: {integrity: sha512-xxbzUeEU0rNTenwciNutB0S+2Std5txq23UTJjqcjI2vDDtWHp5AfFcPr31V1+aFfnVj8G9wUCsyqpxP9XqNEA==, tarball: file:projects/ai-assistant-resources.tgz} + resolution: {integrity: sha512-VLxY61OeR5xDU1eSqdKlYBnTpKtz+D/hlIcHsghiYuw4rfjFXcgt6ByHsLoJMUpYPoLTAwdSkKHHkExoYD8g/Q==, tarball: file:projects/ai-assistant-resources.tgz} version: 0.0.0 '@rush-temp/ai-assistant@file:projects/ai-assistant.tgz': - resolution: {integrity: sha512-udLy+dlOrRFUlOgEvTPwUYqP2lQG8fZVubCtwTejLkewZSolRwcAD+rAwKVKp8gu1ZlXO0P2C1CSl9/k96HGBw==, tarball: file:projects/ai-assistant.tgz} + resolution: {integrity: sha512-DsZThE4ASDoVo5SmdrfFS757hR3TLCriyNdO+3L/Er+RTnQNPTJVRYyzxinW57vSEKBx+RkRmw85pVnbpb43Gw==, tarball: file:projects/ai-assistant.tgz} version: 0.0.0 '@rush-temp/ai-bot-resources@file:projects/ai-bot-resources.tgz': - resolution: {integrity: sha512-ZcZODDqE7Nopg5wL8YkO0B6XsiR+ZjUg2jbKYdMVfwDAu4Sbymn4bhnFO8+ZKZpGAOsy9cJEAc+q/evsCqikPQ==, tarball: file:projects/ai-bot-resources.tgz} + resolution: {integrity: sha512-h5Gsa2ZwuhDUpcYNC0ho7+PLbT/9842b2qR33DT3FB0h1uUIzcCJGeOvjtliDL4u8X/OGCeM7Up6cSd159DFxQ==, tarball: file:projects/ai-bot-resources.tgz} version: 0.0.0 '@rush-temp/ai-bot@file:projects/ai-bot.tgz': - resolution: {integrity: sha512-umT+ZQjrtFEQDaLRjimd7U7d36o5ixP5dBLGgmfwXO8dXDWaux/k2G2iYtRajTs4ptvqVUzDvGauk4vtAyLxew==, tarball: file:projects/ai-bot.tgz} + resolution: {integrity: sha512-Pt3HOxCVia8PJrGId+co6Hue0gBrfmV0VEuQnCjoUnkwOTZ35EWfRj/kg6gryHUuDz9azbpwxNQJ2vWQGjmovg==, tarball: file:projects/ai-bot.tgz} version: 0.0.0 '@rush-temp/analytics-collector-assets@file:projects/analytics-collector-assets.tgz': - resolution: {integrity: sha512-/VLhcRgyAFNmcDOxCyVH8ObGJBSPpjDKjzO0NokX3utqPRHwr7ZQC6pge4Ty2KVNPgr5473aLABAvWZ999q6WQ==, tarball: file:projects/analytics-collector-assets.tgz} + resolution: {integrity: sha512-baIdeQgs2r/fkcI6uvg2eCskFiw4+8PsXPTTSGbUTSl4HVf50cqiiwAFyG6+fyH/Ourmqk5BpXKwdvNTFsfdfQ==, tarball: file:projects/analytics-collector-assets.tgz} version: 0.0.0 '@rush-temp/analytics-collector-resources@file:projects/analytics-collector-resources.tgz': - resolution: {integrity: sha512-VdWSU9lp1EZ5M4O2Xr8iX0g+qSgTclWKi1eZGxB5rxOrwbsL0cwNhDM8Kq4023NhTw/2axpTVtYui4yr76mlqA==, tarball: file:projects/analytics-collector-resources.tgz} + resolution: {integrity: sha512-cchWy0vu5eUKYy1Xx1A3npzHfD2uzQn+Muj7JvW+8D+BY52HqByfDm3/0ozNxO848hW9Y9nh7P3Kd4VOJvDK6A==, tarball: file:projects/analytics-collector-resources.tgz} version: 0.0.0 '@rush-temp/analytics-collector@file:projects/analytics-collector.tgz': - resolution: {integrity: sha512-CATmmI+w2I4N8tl6Ej26OMLF85141t+MrHPRv2X1IKXyLo2AylrHB2FtVDW0XebQCQhCQaQ3IqSZcpj6NT1DaA==, tarball: file:projects/analytics-collector.tgz} + resolution: {integrity: sha512-LLxrJxiQhyXBEq6No2qg+bWdrAPQ2h6vB25iMBLf3+kU1L4ESz4y8yxbqV8NTRmueav70bjJtZU/X1EheFQXTQ==, tarball: file:projects/analytics-collector.tgz} version: 0.0.0 '@rush-temp/analytics-providers@file:projects/analytics-providers.tgz': - resolution: {integrity: sha512-Sbm+sZgUcYWXxq4JqrmusujpDWIWunln71hRGmzZ+SLl9N34KqOGz8ii33Pi87HIwziju/U5Ge9aHyjULbvjLA==, tarball: file:projects/analytics-providers.tgz} + resolution: {integrity: sha512-zaAf2A/xxNOHQbxpPvT77eh9s38Jw09afZl9CFqSeI/DUji3BGZol289d+FZn78OZkwjTYa+cKdGsuIZ+Jeczg==, tarball: file:projects/analytics-providers.tgz} version: 0.0.0 '@rush-temp/api-tests@file:projects/api-tests.tgz': - resolution: {integrity: sha512-Mv//c7sryKf6xCq7ThaymmMVV87Y4KAFDUV14Q/2KZLFKBDa8V7Ml8eFAc7beg/ZbtQDOieQd0cYXW6Py6o1SA==, tarball: file:projects/api-tests.tgz} + resolution: {integrity: sha512-lxfR28lRkIparBIZBYYL56p/OzzyIiN8LvNQKFYfkLis6ojmBbyriyPxmblf2EnaY8tQBnyP+WFIcelo+7XqbA==, tarball: file:projects/api-tests.tgz} version: 0.0.0 '@rush-temp/attachment-assets@file:projects/attachment-assets.tgz': - resolution: {integrity: sha512-o9O9UERbgDaRx7JCJldPT/9oXLRNsh9IDv+6ZC1uF+DKL7q0R2wqajogrK/n3LhvU0DyLXWXNI1w9cwYAfJR0w==, tarball: file:projects/attachment-assets.tgz} + resolution: {integrity: sha512-rUqToJpz5FI/f12t0lksSqCjUm7CNy3BVHjNJ/xf6oejWS/RbCv72PLJwhG1lskhznxAuxXyM2YXJQde/RaZGA==, tarball: file:projects/attachment-assets.tgz} version: 0.0.0 '@rush-temp/attachment-resources@file:projects/attachment-resources.tgz': - resolution: {integrity: sha512-KOxsWkGjYJJo/ux04Q3VzQ7gb2bKMjBaGvAVI0lAz2IQpOpBILsVNsnPWXhWo/UoO0P5sD9FAMYFWJ9r46BouA==, tarball: file:projects/attachment-resources.tgz} + resolution: {integrity: sha512-2rc1LETwiLSJS+9LC6TLpt0JBLhGW8AlAu2DVkiRBSSBdzG/k+wOxHuZD9Kz7+dZmCpnUKY5rB2T8x55spU0Xg==, tarball: file:projects/attachment-resources.tgz} version: 0.0.0 '@rush-temp/attachment@file:projects/attachment.tgz': - resolution: {integrity: sha512-nDd5Svx7WL8NUoMUvKmaGgDymHLX5Qq6VFwzSMx7JQnnHRHBK8Z4Ca5o3N3QY9RcFczG0NYYXJRy75htwvsOkA==, tarball: file:projects/attachment.tgz} + resolution: {integrity: sha512-gy4m+UJBl3oCG2Lco0ynQzWYctvc5X0yf0hdc0hmvWvcq99YQWcqGBpjsrJjsFaflgmlPSeeTWDQWGiKmj8/6w==, tarball: file:projects/attachment.tgz} version: 0.0.0 '@rush-temp/auth-providers@file:projects/auth-providers.tgz': - resolution: {integrity: sha512-CYVU5vKdkv09eZJNTgW2n9SUjbPgSEb0jc7tXes/I4g59m3bGM/RdLMN/yYCI5KQ6QyxzBAjWRBtomo4yfaNFA==, tarball: file:projects/auth-providers.tgz} + resolution: {integrity: sha512-DiPEVZwFsSkPLaHKQ9HWpoR28UGtsT/cuvnVk45tSG3sjn77Ikq86foDMAjNjEG/ekt+keG/bi+Wm8x3CihMQg==, tarball: file:projects/auth-providers.tgz} version: 0.0.0 '@rush-temp/backup-api-pod@file:projects/backup-api-pod.tgz': - resolution: {integrity: sha512-vmUB/aG8KvttcSwHHZCrj8w1ZGIepP7tm8YZbwvBcLkNnvN8wBFX0AUtlYIdwJhpOO6WQR+q67M0goT/4ziTUw==, tarball: file:projects/backup-api-pod.tgz} + resolution: {integrity: sha512-YqU50hDYKuaI582KgjvHo5F/LuxAO9PwlvKt7Pa1lC5xnsLARBfeszHss2CyQ1ZemPRJ+snHFiHuamyrtB/aag==, tarball: file:projects/backup-api-pod.tgz} version: 0.0.0 '@rush-temp/backup-service@file:projects/backup-service.tgz': - resolution: {integrity: sha512-Z8iLROV2pQ+sh5TfihD4GqqYTVcGxYTYbS8cP9xWZccdofKqq+OI/Z5HN3vdl0200pFQDFWkAn+FaYdXgvVmbw==, tarball: file:projects/backup-service.tgz} + resolution: {integrity: sha512-cgbZnO1RLNuOk4hDc/lUcjsY1LKLMaz+Qn5DzUIwnrHdXRUeklfFwlf29QJ+mTtcnAQ2vIoEOXMRxWJ4uSqPyQ==, tarball: file:projects/backup-service.tgz} version: 0.0.0 '@rush-temp/billing-assets@file:projects/billing-assets.tgz': - resolution: {integrity: sha512-fTazlLMoB3b+202XSZFtZTSVv7co/BpyQ7SivXHpROQpxOPfDjZ0e1F4c6dUBJFFJDPrekxFEXfYRe0Tp71++g==, tarball: file:projects/billing-assets.tgz} + resolution: {integrity: sha512-Mmf5pxjgs4vnn5w2tQZ3+WPbY0gG3UHIgNevvRtv1FQR9S7QmhSH8G3xIr8/Mj8iAxntOw0xuFJGE9ijKbedgA==, tarball: file:projects/billing-assets.tgz} version: 0.0.0 '@rush-temp/billing-client@file:projects/billing-client.tgz': - resolution: {integrity: sha512-+LFsjr6NcwNhIzgLB/+lJICjJIiMJCLC3teN8JqmamClBAayx4i3MyDOS23j2HxzvCYIT73K+SoPaAUEAPVOVg==, tarball: file:projects/billing-client.tgz} + resolution: {integrity: sha512-115IiQ03aK9uMXNy0UkGctcWO3X5iGJZBHJsp6dADOzOlSq1H5WOQJFcWOSMRKmbatSWn8kQ0o+4SEEq2LADag==, tarball: file:projects/billing-client.tgz} version: 0.0.0 '@rush-temp/billing-resources@file:projects/billing-resources.tgz': - resolution: {integrity: sha512-NvKU5elR8MV2f2gr6kggoKNW6udRJ0ktBji1Xd8LMS4B0W6i2KhOxhoC8zJkIkf+G2RaBmBzuZGPzSt9L4wQ+Q==, tarball: file:projects/billing-resources.tgz} + resolution: {integrity: sha512-eVcniXw0qCu09Lfnw44tTzGETtPsaxq/P+MW3I7uBstKssHZYCpiG+1mB8wNK/Y40LwL51y8isY3J2O8SV9c5A==, tarball: file:projects/billing-resources.tgz} version: 0.0.0 '@rush-temp/billing@file:projects/billing.tgz': - resolution: {integrity: sha512-vM0PhHMD15t3HeYJGSstz8noiwjHdvBRYz8rgw5JIA5VHJyMumWVQiTEld2sCUUtzelDYgad7wY6EPi91nLJCg==, tarball: file:projects/billing.tgz} + resolution: {integrity: sha512-DYoox5HytjwdxbTMQB0MXJ7s0RYiJoPZVRpzBJBkzmVEl8FumjNMvwP8dzpaLh6EEznjxZqhwIDyuz/uIjc2WA==, tarball: file:projects/billing.tgz} version: 0.0.0 '@rush-temp/bitrix-assets@file:projects/bitrix-assets.tgz': - resolution: {integrity: sha512-7W94HnHgeLCoLAAxDFGVoQgUTKtCWGrUcUfzZwLaxWn9Y0zFgatlvHgjJI4EVAgUGlcRNcLH2XW0nkNuLA+Fyg==, tarball: file:projects/bitrix-assets.tgz} + resolution: {integrity: sha512-hrizM1D8u7scZJGa+PqzQPbmL1anZ9cvdbUjtsciuUZH4hj7rHumqdnVxzSfAe27wr7dWs5BknGUTJ0sTfjnzw==, tarball: file:projects/bitrix-assets.tgz} version: 0.0.0 '@rush-temp/bitrix-resources@file:projects/bitrix-resources.tgz': - resolution: {integrity: sha512-sbu2+csueBPSHdJvLFP2W5eTZofdMRa6Kjkoxs/VwM98FUW3pB0o8NXw88XadiUCjES5rNN0/4gjeeXnNr0ewg==, tarball: file:projects/bitrix-resources.tgz} + resolution: {integrity: sha512-gdyxCI6VcEXdVYmdY4Lu+2EAA9W6eigqC6a9yNfdZVRkmXdVg/SsmuJ9E7LX7Lhkf1rw6JbeM7V/F07QLOXmGg==, tarball: file:projects/bitrix-resources.tgz} version: 0.0.0 '@rush-temp/bitrix@file:projects/bitrix.tgz': - resolution: {integrity: sha512-eeSBmKBkHcoMItUnL6EGsjZMwAWIwFpy9PAXnOUw6x9BRbI2FcKgIokBnUz/q0SK99FcdbfTguIG3t4uKyOqMA==, tarball: file:projects/bitrix.tgz} + resolution: {integrity: sha512-IU6q/JZA9e/YzGual9axXgA4zf1gSM7/KNKRdh6BQ9uCXHX+gFZbyl+F1ZftiMk9jFQ7lMGCKTU0Ek8abgG43w==, tarball: file:projects/bitrix.tgz} version: 0.0.0 '@rush-temp/board-assets@file:projects/board-assets.tgz': - resolution: {integrity: sha512-AP7kvy0TsPeol1IFW+/gOar+b+HwcYU4/k0jrYbo/91PHxM099fGMPZIGW+86cfVGqCIhwVlnSBJ2yxagsXtfw==, tarball: file:projects/board-assets.tgz} + resolution: {integrity: sha512-fNAc7hk8MHaWwn8DPlcqRtEoBWm+aCdKRKkmTOxidawZEA+rM0iA6yg/rVAd3gEenVetTEeXPtdn1Td7Dzd6dA==, tarball: file:projects/board-assets.tgz} version: 0.0.0 '@rush-temp/board-resources@file:projects/board-resources.tgz': - resolution: {integrity: sha512-viBQzxhTLbDFuj6ZIifGh/ykeJkPE6koKw46JEk9a7mvQcNwjjlXcx3+7aX0w+yMvkFDDam2XHQ9HMpyzsJS7w==, tarball: file:projects/board-resources.tgz} + resolution: {integrity: sha512-WK0n58SiB2vZOxjHu8TMgZJ9hYX3++MGTbVcybHQwDHXk/ZfKDPtV5H1Th8kqwFf5aoEigA0ie7qF7V2Ds86NA==, tarball: file:projects/board-resources.tgz} version: 0.0.0 '@rush-temp/board@file:projects/board.tgz': - resolution: {integrity: sha512-dhT3leQ7DW1z19TnTz98NqOrA9TrZBnF+A+NwrBykUg3C9gA6GmRmwmoSBiYwsIr7gPZheq3fgQQhYFfWtwc1g==, tarball: file:projects/board.tgz} + resolution: {integrity: sha512-AefrDolzwlxbmWDd4BkYGfvxyNV1sHiBTDi9+GULPN/lbKUL3kwO9T8LNkkFbZwxYlI3k6Qkf+3tfqqF9qcJOQ==, tarball: file:projects/board.tgz} version: 0.0.0 '@rush-temp/calendar-assets@file:projects/calendar-assets.tgz': - resolution: {integrity: sha512-ZVny2jQO9EPAzHlJZzrBdGv5cjARx6redl/5n2JjBwul81F578YWf0qd1NZB8sMPzY3OXqAfJy2Si4U3AOKsag==, tarball: file:projects/calendar-assets.tgz} + resolution: {integrity: sha512-iSdUPnA/NaUmdNZlGz5HQDMDFPnm/UB1o+rCTT2RRc+vF+Hboucvxqnth39OHW+sr2u94QjQnSwISx1fB1XGsg==, tarball: file:projects/calendar-assets.tgz} version: 0.0.0 '@rush-temp/calendar-resources@file:projects/calendar-resources.tgz': - resolution: {integrity: sha512-zB12rYM6ssTJX3w9kbfGRjV8luGil0pMK19EJCBEmqBrHAEjCGj70JXlH0QEvUPxxyM1oKH7Oggrw4Cwlmsnug==, tarball: file:projects/calendar-resources.tgz} + resolution: {integrity: sha512-FM3dkBbCcvvR6BIpXp7Es1KxKtaLh2uFPS9HhUCURiCocQwfhW9Z7NbEHoRMz/9al7FU9YKTeWluWgODV6LCkw==, tarball: file:projects/calendar-resources.tgz} version: 0.0.0 '@rush-temp/calendar@file:projects/calendar.tgz': - resolution: {integrity: sha512-RzDIt0tll31QwvSknkOjtV7xKlNUvvZzu5RPKevrS3Ia28J8HAJohqEGeBcRC0yPzMaAHwaCRnTzPiFGM8sXkA==, tarball: file:projects/calendar.tgz} + resolution: {integrity: sha512-BeX/c/tGHtc367xOhGU9WDeTFlFVobaaHQ8yiDPGK5UW961Gz+58F656egWRGQNjcwm9pKH4fOQZ8+MBM7FxUA==, tarball: file:projects/calendar.tgz} version: 0.0.0 '@rush-temp/card-assets@file:projects/card-assets.tgz': - resolution: {integrity: sha512-sJk+j/J20kcA0fObr+XdKFjbNs9XTVJ9Sy93UhZ0+THPthqpBuGH57BFX2w6lVkZ08gl8DHEixZura82uqgXOQ==, tarball: file:projects/card-assets.tgz} + resolution: {integrity: sha512-CbHUGxuvxNP8VhzpLaUySqIi1qiXt7opHxzliUfLS1Jbqj2qG/GolTYzijbhT9M+tXEYApvlXZX6ILP5EbQgCw==, tarball: file:projects/card-assets.tgz} version: 0.0.0 '@rush-temp/card-resources@file:projects/card-resources.tgz': - resolution: {integrity: sha512-cXeSj8QZTZdnoBvVJ9G9ZyE2Ndkt3VviDkRDxLEOe+ewJLOSRJUWnK/+IkAkSnUQItI4c76i2k2qxq6SkomOcg==, tarball: file:projects/card-resources.tgz} + resolution: {integrity: sha512-QiNduzk38yooG8Lk82OxRaP7DNXXcsd7fKUPE7j8RM8vlXLZMLD/Qap0j0n14j02wV/Cw6RFro9SBZMJHGuMYg==, tarball: file:projects/card-resources.tgz} version: 0.0.0 '@rush-temp/card@file:projects/card.tgz': - resolution: {integrity: sha512-kYNj57GzcJkOPmkqf+im3dqau+y1EZVB8KTRBVDdJqa8ClO6e5kJsWaCBeiFuDr/htCOqRAdErL8BVdMPxlHmA==, tarball: file:projects/card.tgz} + resolution: {integrity: sha512-X9ZwuYcOYwETMVVxO80p1o+cjXBo72F2ajNlakF+ARJoxaOavJchN5cZm/kWg/MWNg3o5iSHXym5VlGqJUDThA==, tarball: file:projects/card.tgz} version: 0.0.0 '@rush-temp/chat-assets@file:projects/chat-assets.tgz': - resolution: {integrity: sha512-/kwOHpBT9ZUW/YKdIbi25116xNHlfiLy6KH9flxR8G/NPsURTmJ5tL23vaOfvjBM2cHf/BnLxBI1FcyQVUTGxw==, tarball: file:projects/chat-assets.tgz} + resolution: {integrity: sha512-sl74Gl0SJf8EMLEiGxaEZ2Qo2a46w0q/mfSlPyDObD0LxPC1Di2GErJM7kRuXfugLt6Pr4Qbp9q1w6YBnt1njg==, tarball: file:projects/chat-assets.tgz} version: 0.0.0 '@rush-temp/chat-resources@file:projects/chat-resources.tgz': - resolution: {integrity: sha512-j2iklV6BhLNnLSVobezUIInA/glyu4Zmh9GnrPypwMC5otRUWvB1BcRsnQC8n8f81lH/dh2X0/KyMIWqRGEdzg==, tarball: file:projects/chat-resources.tgz} + resolution: {integrity: sha512-iMfYtYIWvyrHk7hjcN9iOUH+MEGu2JHUqChgJz3qX5zrp4bONRbQItPK4ELAAVcz5xxILrzIY3JYdLOntOrI3Q==, tarball: file:projects/chat-resources.tgz} version: 0.0.0 '@rush-temp/chat@file:projects/chat.tgz': - resolution: {integrity: sha512-ikLU2dDEj6DJEsEGKbQWxNmHl3yGiRfzCJZtoK8JMvJy4snxIgaeIZSllLsAJfdpqb+AP4FAYFd6+vjR6K9SSQ==, tarball: file:projects/chat.tgz} + resolution: {integrity: sha512-K866FbglO70pAG0EZl7toVkYzagfLDp/8vnAG4E0TX5o/xEltCrjZyphCxXfJR8l1Je/hBHPe6H2gAGX+ZBTag==, tarball: file:projects/chat.tgz} version: 0.0.0 '@rush-temp/chunter-assets@file:projects/chunter-assets.tgz': - resolution: {integrity: sha512-DTYAH4LA5KxtbmCfItFcA47+qMQMQSVjM7x0PzP4lfEp/TXFD5jVafkYH9GZVIb7VzUVnGiuCjR1aj5UQH5OBw==, tarball: file:projects/chunter-assets.tgz} + resolution: {integrity: sha512-sIrymSpJFkAjKrirPTzmtXsX2ZVFsF7te5B3Y6d64aiqUYeKLwJJvgc39jLF+klU8sMHq7xww4iC+YCXY4++JA==, tarball: file:projects/chunter-assets.tgz} version: 0.0.0 '@rush-temp/chunter-resources@file:projects/chunter-resources.tgz': - resolution: {integrity: sha512-fTgQUBmVUl93inv0Y6EqKhTOqUMdepua9ps0U0w9kxKIaXfJXAOTuSfEpI6AMQ2CyZSmQ+pWYb/juSRwyvNDWw==, tarball: file:projects/chunter-resources.tgz} + resolution: {integrity: sha512-jPueJbWmWeGpKWWYliHQDodMSD0p6ISVNfSwr5lMI8cR3TGJ6iwZffopJwio8dT26heLmu7He3YKS9vmas9dow==, tarball: file:projects/chunter-resources.tgz} version: 0.0.0 '@rush-temp/chunter@file:projects/chunter.tgz': - resolution: {integrity: sha512-oPud9gb+43M0a+2rIdeB8T8fwn5xTqJyt+QXN2nelDxEdKBegMkKzDctoRg7Dniol1Xeph3KCvMECu1p48f/+w==, tarball: file:projects/chunter.tgz} + resolution: {integrity: sha512-V8BxDWQI1SSa0wycWYXQxGidlIa2wbd3u6IJi8O2z4pW+1SGA0p7XMVLAsu4JRxNbXJxJbwFh5p8zQq+u8Q6cg==, tarball: file:projects/chunter.tgz} version: 0.0.0 '@rush-temp/collaborator@file:projects/collaborator.tgz': - resolution: {integrity: sha512-d+s3QxvDG2IuvcqtuTbcESw+q3eBSEcW9WcDQIhfHhOSLPcpyvpSd5pSADFxoXHX4G4aNtXafvRq5Bv028m7tg==, tarball: file:projects/collaborator.tgz} + resolution: {integrity: sha512-XKdx0129wpIdHFt/MVBfZrQGvCAEjYo9/F6Dbj1ObxUoAJZX0Dy+zugLiwlLZvVWWivawCb8fRVLi7uF/IKHIA==, tarball: file:projects/collaborator.tgz} version: 0.0.0 '@rush-temp/communication-assets@file:projects/communication-assets.tgz': - resolution: {integrity: sha512-Wal7oY3tilhgbPPu+PTlPKz+cfmbdCwbIchlUJdrKaSHXffVDRBJeh6qpyNZO2732Xmizsqei55Nl408BN7I3A==, tarball: file:projects/communication-assets.tgz} + resolution: {integrity: sha512-3EclzY8s+UDjEoAiYq3WXTje7zgetCk2KHFYNnnnbmDHYrKTAdSZA1ljAJlO335Q4RJLUoLLyQNYwEmuUKkg+A==, tarball: file:projects/communication-assets.tgz} version: 0.0.0 '@rush-temp/communication-resources@file:projects/communication-resources.tgz': - resolution: {integrity: sha512-JyZyInUv8v6Gwi7tHJXdM6xo+1KM7Us9+hNOTuSIf+/uVuZZjnbeNr4qnmqRzAi/V5taXq+IvD2pnBWZ3zmk6Q==, tarball: file:projects/communication-resources.tgz} + resolution: {integrity: sha512-nR/kRG3fV0pobKlUJTJ56wJ2v4d76Ag99SDqXuPv5WatWMnSjR7SEzJiZfcxVT6wyzRJnjsEN14vvMkLBL9uRQ==, tarball: file:projects/communication-resources.tgz} version: 0.0.0 '@rush-temp/communication@file:projects/communication.tgz': - resolution: {integrity: sha512-OaDi7rN0T5zfwjcrJFrOHZ7ojNrsxZIdevJCl3k87Qu+fSTCmvVuFe/tPoEIEpwWkK0jSInmdAZhXM8RaC8UcA==, tarball: file:projects/communication.tgz} + resolution: {integrity: sha512-hvW6Yo2rm9xpq+kyAnt6A8qvXbauSOM69EcmX0WDIdGFcu+NVPh9HARuZ2/LMuCmOCOWSnzzwOHeTim+gfW7+A==, tarball: file:projects/communication.tgz} version: 0.0.0 '@rush-temp/contact-assets@file:projects/contact-assets.tgz': - resolution: {integrity: sha512-XSuCb5//dbmZ4ep7dIDZ5gWJ4wdgZJEtkYLzWlOhuIYpJmbILNDIr+4vGQpGgvkNrdxqKNjNRi9mf6nLOupK3Q==, tarball: file:projects/contact-assets.tgz} + resolution: {integrity: sha512-wr3lxynsVCyU6gduEz6L7ik0eGHEpACxag9XK1OWi96+Fk+VL05Lm8wifuIRGcqmBN7DcSZP7LJvDDV9ItpsDw==, tarball: file:projects/contact-assets.tgz} version: 0.0.0 '@rush-temp/contact-resources@file:projects/contact-resources.tgz': - resolution: {integrity: sha512-sCwizhdBR25yoec5x805nxca5aqgx7oXbMDajJ7kQR2I2D8Yy0Q+8JEDwmgxxX/l9AoPwqI0kl+IH5AxsB/Vcw==, tarball: file:projects/contact-resources.tgz} + resolution: {integrity: sha512-1sxdoHqoQP90DIIsAHcH8UYMtqso9APn0RrXyk3Gn7PIxQWdacG4j8t5El/jaR460JV/s7BC+OiNt2GAWek2Ew==, tarball: file:projects/contact-resources.tgz} version: 0.0.0 '@rush-temp/contact@file:projects/contact.tgz': - resolution: {integrity: sha512-UfBOBXkkCHR7iUHssMzpn76rDE1cpWWPDzIvqF7CRKz7XIdkxWlgVP3LySxk5h7FiMBb/2YHoTW1Eo9L4VN1UQ==, tarball: file:projects/contact.tgz} + resolution: {integrity: sha512-K1vyUZEeoxjcG+BwIjH5sm3hF5kJnmJIR4BqbNdsdfzQK8ohC95tNPMCmKEOk/JeS3KZbDz/Ryd1MpQmMBitDQ==, tarball: file:projects/contact.tgz} version: 0.0.0 '@rush-temp/controlled-documents-assets@file:projects/controlled-documents-assets.tgz': - resolution: {integrity: sha512-sDve2JtwVF1/7A8YZtLV88hZ94UMVjgRsCUPhhp3yTdN1eYVzpC6zuwReYCczL0tZPnlNDnNNxBqDo3BKsVO2g==, tarball: file:projects/controlled-documents-assets.tgz} + resolution: {integrity: sha512-cHZ7mmamaP3MVHYJGk6/33it2XlDcVJ3bZ2xMQKK8ZVzLXHv0KAzJ6rLOV2F9CU9r+23ZolteITQ56M0ESfAHg==, tarball: file:projects/controlled-documents-assets.tgz} version: 0.0.0 '@rush-temp/controlled-documents-resources@file:projects/controlled-documents-resources.tgz': - resolution: {integrity: sha512-k1tShV0zadQZFo2NU4Je7neRr5S7l3dXRn+PFYH6D9ORKK2tGc37afvYpj9NBmndZ5ch6hLPiNlDFnFy5FNSPA==, tarball: file:projects/controlled-documents-resources.tgz} + resolution: {integrity: sha512-PfdfNUmhFvCBMVYVDpEyxjOorfo9PhEQaL0MbQSrJ7VQKjyorLC4VpkUaqCyT8pWolgOr/hkUZ+ni1fsCJXOTg==, tarball: file:projects/controlled-documents-resources.tgz} version: 0.0.0 '@rush-temp/controlled-documents@file:projects/controlled-documents.tgz': - resolution: {integrity: sha512-rxSyYV7I+IAOhIPlESyVhgQobWUz2HKHVDXYSCqP7cbBT+jdALJCrpoLYMr5/YAWS1bkupMa+fhywmHvFz+uWQ==, tarball: file:projects/controlled-documents.tgz} + resolution: {integrity: sha512-gb7+gmV+eLzjSuRuG8S+ERMJnL9CAV3uaUGVAjVul8x1Xna0D3L1XIe37EoyKSBmO9JDARP2bZHxCkavDyWa0Q==, tarball: file:projects/controlled-documents.tgz} version: 0.0.0 '@rush-temp/desktop-1@file:projects/desktop-1.tgz': - resolution: {integrity: sha512-9ZoQBuxP84rhaJ4GLDNNICIxVMNFgo/+bU5gWfxbjApVSqrqmcPouzBr8Qxrv9Bh+8dnoDdvSGB7p318PjVTDQ==, tarball: file:projects/desktop-1.tgz} + resolution: {integrity: sha512-7gDTrCEaD+WzcjEUS4d45SkBgcNENsWmGdgpeojHHq80ACYWac97qK0lnv+nSv9pnQw4xqyghQHqQLHgh5d1RQ==, tarball: file:projects/desktop-1.tgz} version: 0.0.0 '@rush-temp/desktop-downloads-assets@file:projects/desktop-downloads-assets.tgz': - resolution: {integrity: sha512-nA44vkQM1JnpLoVPC+bT5idKeAxnfhNPjfLnen5MJrILKvqgXvG48gfU8CJB3gIPblqbi2DzeV2Sm6mOL+oeYg==, tarball: file:projects/desktop-downloads-assets.tgz} + resolution: {integrity: sha512-ngGhvxH1Bg7psl3L5Gu97eiXar/koWEdhotk8lfwufJpcd5/6XhKjPiyQRl/q2/IKnCTrvOC9jn6L0II6m4qew==, tarball: file:projects/desktop-downloads-assets.tgz} version: 0.0.0 '@rush-temp/desktop-downloads-resources@file:projects/desktop-downloads-resources.tgz': - resolution: {integrity: sha512-Sv6+oPOnhmMb4rbW4WikRmM768OTXrtT1TlpIhMMXDm6rkO313e76iX+nJJMDQWyaa19+EN0OorssEyqXGPunA==, tarball: file:projects/desktop-downloads-resources.tgz} + resolution: {integrity: sha512-XouNzthhrEEMcifuA9JLbLpb0V/tAl5cqqnjFnPBFFcVJUBHs2lDdrXBZe/wy1QRRAbAw3DRQwn1wHVqlUfwIw==, tarball: file:projects/desktop-downloads-resources.tgz} version: 0.0.0 '@rush-temp/desktop-downloads@file:projects/desktop-downloads.tgz': - resolution: {integrity: sha512-54meJjU1TB/k2ZHrnX+dznoXeD5/zK2uKjmL8JJNAjRvYi8Kc6M34zf8/ba6hLacu1UKbJFAdWl6R7j6BF4R2A==, tarball: file:projects/desktop-downloads.tgz} + resolution: {integrity: sha512-zsyTJRgXSUgr8foxaJ1+2XQeH1pX0s38h5Gnk0r2sFkXxVVM+OMpRh4Jd7dkfNHrg7L/zuCQOL8v5WP8flJ45g==, tarball: file:projects/desktop-downloads.tgz} version: 0.0.0 '@rush-temp/desktop-preferences-assets@file:projects/desktop-preferences-assets.tgz': - resolution: {integrity: sha512-I1pPgk1942KDwJyW/DL6+nfreVmv0kuxsgrCVRUKmscfMtavHQPL3z2Lte3ZB3i9659QwSADalyD0kKOcF1aug==, tarball: file:projects/desktop-preferences-assets.tgz} + resolution: {integrity: sha512-CF0J+7Ch1yBgYk2QfAwfx9uzAHGG94G0K8pD2NqPFRAYnqva66YuVlF2Q9X6h+1sNoD4JGw9g6MxkQRvV3FoiQ==, tarball: file:projects/desktop-preferences-assets.tgz} version: 0.0.0 '@rush-temp/desktop-preferences-resources@file:projects/desktop-preferences-resources.tgz': - resolution: {integrity: sha512-LUDEZ/vhTvD7t17S8J72xkQ3D60mxEImoVu7uey4TCaE7RnxoVD3e+ynmq2c8E5bl7NpwrAHKGMVo9bTCwEuww==, tarball: file:projects/desktop-preferences-resources.tgz} + resolution: {integrity: sha512-JFxHZQolJjzXZ5rpLQnhY4W5eongWepJWHzYfXp5ZfTLcY4Y2VCkvl9/DnC/XwPhL4rktX7uLP7z8pDxcnnXhw==, tarball: file:projects/desktop-preferences-resources.tgz} version: 0.0.0 '@rush-temp/desktop-preferences@file:projects/desktop-preferences.tgz': - resolution: {integrity: sha512-S5uaF9Uqjk5nmy8cSri39g46BcasHJ2J++6dZGSktyjY7daHwCh0Zv6RuIncKcnLfAO+yGwki+niItXIxUvfAw==, tarball: file:projects/desktop-preferences.tgz} + resolution: {integrity: sha512-4ZrQBvfPRup+kK2ma8uj5ZCC0qceWog7HEHJXJ2oz9NIGKC9FpZlmqQC1Q0AKjVLpHdrF8rN2HgC2mr8rbJ+cw==, tarball: file:projects/desktop-preferences.tgz} version: 0.0.0 '@rush-temp/desktop@file:projects/desktop.tgz': - resolution: {integrity: sha512-fenstlkmBK31g3XyvnCcniFUIDpGDGQ/gklfgDQtodVsadiLS53mI9cG2kwAvodrXno10v24JyWEOjTHUpd9fw==, tarball: file:projects/desktop.tgz} + resolution: {integrity: sha512-yG5KVGVNrfLILglJARviTJgENNT7yHPyXgj6W8QFQlSqEjLvPY9cWVTgewXXaWr2v4/3ZuzqGXy08kuPHcO8eQ==, tarball: file:projects/desktop.tgz} version: 0.0.0 '@rush-temp/devmodel-resources@file:projects/devmodel-resources.tgz': - resolution: {integrity: sha512-M2xxib4kIulQympkQs5BZKaKL84fusZMbozup3Z6x/AeKoOxR8TP9J0Ec8I8AHKB64zynwgn/UIc/sVFbdiByg==, tarball: file:projects/devmodel-resources.tgz} + resolution: {integrity: sha512-Dcb5ufRCsX2jgkSIzMlAeWmmkxXup8Izzn9gD41zCIJcOe4K9fmrJDSpoMKg3J8LmCaJ/VKR65NpPamUr9VhkQ==, tarball: file:projects/devmodel-resources.tgz} version: 0.0.0 '@rush-temp/devmodel@file:projects/devmodel.tgz': - resolution: {integrity: sha512-cYB89W8tqQJu4Zo40zrEyMY8LaOVDTpPUJL112uNE0Vii8VbOFbgLd6LbsMDzRLMCKHCoXmsU69cwxQ94zJN3Q==, tarball: file:projects/devmodel.tgz} + resolution: {integrity: sha512-O3+E39dnDwCiJzikCPIljB1jARPyC1tBcYHvCqiYIq6Y3QnIof0DAlhWaOyMEpsf8K6BnLgxb4nRcK/up33B+Q==, tarball: file:projects/devmodel.tgz} version: 0.0.0 '@rush-temp/diffview-assets@file:projects/diffview-assets.tgz': - resolution: {integrity: sha512-bgErXpU11dTqsM/i9G9pAyE//i6z5zLVzfzFgOJ5lPyZT5N5eZPrXvOthJgz4QAWn+c7XhI+X0IUprhS7GDvCQ==, tarball: file:projects/diffview-assets.tgz} + resolution: {integrity: sha512-qjiyOjf35S1YljUGE6wOC9X0TwJ5T3NA2c5vCKqzySyCtKiL+j4QIPRboSNulaQ27eP7bR2xhPoZbd2lCZ0Izg==, tarball: file:projects/diffview-assets.tgz} version: 0.0.0 '@rush-temp/diffview-resources@file:projects/diffview-resources.tgz': - resolution: {integrity: sha512-ff6Tfmas0YYlhoTvuZaRUZjDV0Ol5ikDBgkb64RBLKhTtaJfcTpF16N68zWhah/8rOxIXcz84mb9h7MTvv18Bg==, tarball: file:projects/diffview-resources.tgz} + resolution: {integrity: sha512-e7INuk9Zu4dN5TjMuvFdvIDFx8yRS+9btJlYRp0Foperb6VN5ZNvW1J7oLYqivO3vUd2NifL/0EvgTOCNBK73w==, tarball: file:projects/diffview-resources.tgz} version: 0.0.0 '@rush-temp/diffview@file:projects/diffview.tgz': - resolution: {integrity: sha512-CFK7D14xauSecbTqxKDyGjs2V6GQLTAlRz5U6xrYIMS7JmRq7xeD9f+xkNpHXdNBOhGRgiU73FnpjH9sE6KNew==, tarball: file:projects/diffview.tgz} + resolution: {integrity: sha512-OYeDjR9cY5bXJHO+fd4jpYNqMIrSMfffFbP6QV/uDGtLf2682nb6unU9kEungE++w6sdzUz5cCYklNWH1Yfx9A==, tarball: file:projects/diffview.tgz} version: 0.0.0 '@rush-temp/document-assets@file:projects/document-assets.tgz': - resolution: {integrity: sha512-UdBbzDb3gswUetNLq+HY4TKXXweJJLdUINKeIaV3zng0kWYUgdSkhDN5QKlodAeZ7T5NuwI2/46dd3ziU2CftA==, tarball: file:projects/document-assets.tgz} + resolution: {integrity: sha512-bCoHQSRYMBU63R+NmgAifT6LaQfQRL1fbM4QqURiN/S6IEbkyYAbHdksBajtCAusLfDJ53d/cTeJanANIQqoHQ==, tarball: file:projects/document-assets.tgz} version: 0.0.0 '@rush-temp/document-resources@file:projects/document-resources.tgz': - resolution: {integrity: sha512-Y/guc5x6ULbh2wShONvdWOM6p3ANhk+RjP5YBQ0TBAUnlm08MVquqsTLtA+65mvYmpFhp8ytk4Sqa1QtMgEDEg==, tarball: file:projects/document-resources.tgz} + resolution: {integrity: sha512-+XC80I9LDJZ4klj4dOl64THMGM40VCAYri2thQVkxdgVXGHFvnyHFBa1Cg1vDawNdB42X2XnLc5/Y5hwa+DQ7Q==, tarball: file:projects/document-resources.tgz} version: 0.0.0 '@rush-temp/document@file:projects/document.tgz': - resolution: {integrity: sha512-iByJ6XYJTatPSiiegUMnDjZJEQhXfGVyzBIS9RVcN6jPiOKLfiev8avT9L9yGg17mG2O+b8fIPQt/pgG7iXQbw==, tarball: file:projects/document.tgz} + resolution: {integrity: sha512-FeoHg7H82o9MO0R00N0ruHv/efCZszXLZwQ1D4euf4d8/Wlj2865LVExuFSMlBhfWbBMgrZuVTM+2LglIGc/Ag==, tarball: file:projects/document.tgz} version: 0.0.0 '@rush-temp/drive-assets@file:projects/drive-assets.tgz': - resolution: {integrity: sha512-8wWlJAbs2fvVg6O19KsL7w3YurvFAAqlICEEaOscgmBYxeu8VAtW70cveFLHFgqnNX5g79zzp6WFaFbY+kZa4g==, tarball: file:projects/drive-assets.tgz} + resolution: {integrity: sha512-8W4W1MOrmIXl6w4qU0Rg44yPcMmjsdSrgIZ1NdJVutNWENlWol+jzrdpL0vEb5qMi8JOsoNdqhTmMNQYFiQzDw==, tarball: file:projects/drive-assets.tgz} version: 0.0.0 '@rush-temp/drive-resources@file:projects/drive-resources.tgz': - resolution: {integrity: sha512-1Ho2zNaf41Vjiy449etblsd76/M67vPmc/KKNCUzsCw9l3JTQY9n27mrp/PTmUVgS17SAN0xYr0BzblY/1yrZw==, tarball: file:projects/drive-resources.tgz} + resolution: {integrity: sha512-HPhT7mQh75hHAxnss4sj70B30WKFMySNerS0fU3kOj7kMqcFIko2LZK+1OILLbUH/LYIoJWKKcaglS+zACyX0w==, tarball: file:projects/drive-resources.tgz} version: 0.0.0 '@rush-temp/drive@file:projects/drive.tgz': - resolution: {integrity: sha512-M8Q87xkwWQz2deENrXH8RIcuCslP9T/jnqLxIkxM+fyoc0aTAFNkONfUlK+g4Oz8KX3edTYYF6jKELQeGA+nWA==, tarball: file:projects/drive.tgz} + resolution: {integrity: sha512-TnqWg4N0mD9JUOZ2yTZzpEKTON7EorNp8wBzAaE/PKfwWNpAGBAOHYJTDaozz5B6RcEasuiW7LAiHeC42uFY7Q==, tarball: file:projects/drive.tgz} version: 0.0.0 '@rush-temp/emoji-assets@file:projects/emoji-assets.tgz': - resolution: {integrity: sha512-XhYNv+h3WWfXqocS3tvIj52at/GUWJ98uoSnwWhDHxHnGMzgSa7YODbqFW4HkuEc3XIABcNPyorHsEJg8v3Evg==, tarball: file:projects/emoji-assets.tgz} + resolution: {integrity: sha512-fqu8zlSIib03rybgc5v10MN4CijwtNC8jo1sfJgo3GiKetQsZDLE7dHYnWYxdloyP/WXri5slEtXBQcZrjpqCA==, tarball: file:projects/emoji-assets.tgz} version: 0.0.0 '@rush-temp/emoji-resources@file:projects/emoji-resources.tgz': - resolution: {integrity: sha512-EBHqx8rISEyOq0YTi6HQqzrrV60Hn58toZKwfAEDn2uBHSMp6+5r7c5PY2PYPs42voNqLlXHO6ccun4v5Dh4dA==, tarball: file:projects/emoji-resources.tgz} + resolution: {integrity: sha512-GpyF/hun3A3HFpBLG/94ty5TJQAFXYjRAdbLxUKRkgyJgKdylPmaannUXkYakRmKoSB/ZR34thhFpCgDnAEaug==, tarball: file:projects/emoji-resources.tgz} version: 0.0.0 '@rush-temp/emoji@file:projects/emoji.tgz': - resolution: {integrity: sha512-9rtSm/xyl8cIG5j1SfU3LUUBobCTjtNLr772p01+Tsc1wF0sljx20KI4CJKgapDeZe6nITSR0nAp2vwnZ92J4g==, tarball: file:projects/emoji.tgz} + resolution: {integrity: sha512-cEZ41qbDdwLT2cyv22vI7Rx4dsbFKTpndUzNoGVabunCHFjkM+LTea7btoY57mDEJ3F9IPq3Aw4IXYdnXN0aRg==, tarball: file:projects/emoji.tgz} version: 0.0.0 '@rush-temp/export-assets@file:projects/export-assets.tgz': - resolution: {integrity: sha512-6xhjdrlZ3x3246D43xkaRx8+4sH+y2KArQONW090aY9TpdLKfbFhQ6AQTM8bOQxnzduF0D79ruypOkxwb7roCQ==, tarball: file:projects/export-assets.tgz} + resolution: {integrity: sha512-7mVBCNfwXmzDuvLhTHG/CsY/VODQwEB2e2gAdj3mCattJBMurWifPPswrAV74dun7/gxNAlzU8KWj7F3ST3/9w==, tarball: file:projects/export-assets.tgz} version: 0.0.0 '@rush-temp/export-resources@file:projects/export-resources.tgz': - resolution: {integrity: sha512-T+ASO3yae2KTW5BJntxztV94gGT0WFyt74h6Uy5gq8M3XVlBXNoQjPJTNy/mR/NlqmgV6YaARqD/o1rBwcu62w==, tarball: file:projects/export-resources.tgz} + resolution: {integrity: sha512-W/bTagJJirv8IJMFBENkGsgapQKK8ZUkSdItSs9+eXNhXkXm3Ubp2SGEhO6AmJv++qXuWTYoSkslxLRF+XeptA==, tarball: file:projects/export-resources.tgz} version: 0.0.0 '@rush-temp/export@file:projects/export.tgz': - resolution: {integrity: sha512-TL8JF9C9w5qgqGVXp2+EKGS0gW8WxxEwnOiIFbEk4Y+cEwr9BeuAr7xPwMA4ZHXz6HzldXGo+f8u6WZzRaztow==, tarball: file:projects/export.tgz} + resolution: {integrity: sha512-cY9lumNJF5jg6QiiHDZiqOPmb2nFzkxijIkFN+X/QhC3mexnsXbjKxoX8Q2smqjuhp3yvJyerBMef+40fIsLWg==, tarball: file:projects/export.tgz} version: 0.0.0 '@rush-temp/front@file:projects/front.tgz': - resolution: {integrity: sha512-hvTfwLKgjNFSoytUYIDdWc6iKl48tEoVwIpPbqr2zg6Z0TnYFx872M3VEoPkR6xZmJzTY253ek7dnFpyG79oDw==, tarball: file:projects/front.tgz} + resolution: {integrity: sha512-gPvcpdLb/meMqEx2HDDFxz8L/O6jguGVE4pwyiqtdt6ybnANcV6vWKqlilxV/s8WELABhmmtPA87Bh+nbfXm1g==, tarball: file:projects/front.tgz} version: 0.0.0 '@rush-temp/github-assets@file:projects/github-assets.tgz': - resolution: {integrity: sha512-mzW5RUh7nGUcw35cUtBn30m7qJyryZIeSL+wK9oFWO12+deEptTzm0+0v7xC2lJFQlaY29FANihsoWOC5jn+Ug==, tarball: file:projects/github-assets.tgz} + resolution: {integrity: sha512-xDiBrvWrBYlBcVQsXgDHBl2hqS95w8h7DrlnM4/j6tOFOvPHDjE1mNvrrMcEQ81m9hsDnjXUFHTdVic8s0ISlA==, tarball: file:projects/github-assets.tgz} version: 0.0.0 '@rush-temp/github-resources@file:projects/github-resources.tgz': - resolution: {integrity: sha512-Bj8BjMMd+njC3PliIyzry1sYyVJDD9G1/2sjsT3000H/yEt1/qtvmhKmcxmVygObShTEO8hPH3sMRPn4ZvsgVQ==, tarball: file:projects/github-resources.tgz} + resolution: {integrity: sha512-ZS+sY/n24OowUq3+tv9DyfVubGTqktN8hFv8Nq3Qe/t2kA9qK6nLi9UQY+sXr9Chmhtp0JW/3Pk8Sz9fsg5qow==, tarball: file:projects/github-resources.tgz} version: 0.0.0 '@rush-temp/github@file:projects/github.tgz': - resolution: {integrity: sha512-uvmnDEKeU8BZi73evEEKAlc/8TSOmKREruZuxO0ziJy9o6M2H/VDTZisJIipVOFROCkSU+nV27n4T9Mug3L3Yg==, tarball: file:projects/github.tgz} + resolution: {integrity: sha512-ck2h1RX82bwxV8NFD9nyfVpdRmutajmAzl4W5JP+jFkYFdV4HBLRmdqUuAFP3GMZTDRLz8Z6lxQytK8NIBcrug==, tarball: file:projects/github.tgz} version: 0.0.0 '@rush-temp/global-profile-assets@file:projects/global-profile-assets.tgz': - resolution: {integrity: sha512-eQ4PTxHMHnqWW4ZAlXvVYTzctgIcjAG1xv9SPSqO4JJ1bMK7Qj56HBQIz1YcKhKb3fsCCrr4paSGBzGDqnui9Q==, tarball: file:projects/global-profile-assets.tgz} + resolution: {integrity: sha512-IXpTf7Oss9NgOVposFRiTM+uIXs3bBaDjTHHeBMc2Mc4DN2l6U0puPSfj4hPrXy9Xb7pwG19CZiORy4Eo+23uQ==, tarball: file:projects/global-profile-assets.tgz} version: 0.0.0 '@rush-temp/global-profile-resources@file:projects/global-profile-resources.tgz': - resolution: {integrity: sha512-rlxgzo9wsSCx8zRNaNQqVHRJSFmZuthWqvNjh6QUabDpeJ92StbaLPC6slCsTvCOSoh0QP4UPVYrIz2bS1HqFg==, tarball: file:projects/global-profile-resources.tgz} + resolution: {integrity: sha512-XFt5JCD+q+kehSEHWY82bHuu9b/cDLLhYsoGw6pvG9j2GlmYoDngDKAg5L1cqoKgFrsVw0faVNBA9fYGNLGxKQ==, tarball: file:projects/global-profile-resources.tgz} version: 0.0.0 '@rush-temp/global-profile@file:projects/global-profile.tgz': - resolution: {integrity: sha512-N17ooAxtFnoGK7T0UjhsrgFSooqE8qQoEsz+6NF7NEiW+0Xk0EfaHlaRKPog5MLD89+Oi1UI5QHSTi1iWEs6mw==, tarball: file:projects/global-profile.tgz} + resolution: {integrity: sha512-h6RYDXdsVY8T3zcZenZ+20FncZ/CUPIk3dOA6ImGnvu5aIVaFSddCowtd34ByOzVKJau6zstLiHA9Z2flrr1fQ==, tarball: file:projects/global-profile.tgz} version: 0.0.0 '@rush-temp/gmail-assets@file:projects/gmail-assets.tgz': - resolution: {integrity: sha512-8SyzyNAI/lbfNTMQ8lR22/a6wt3PBhtooIhetAine7BolCkTzWZ+JH6I/uigQxOSA+gMa7uo3BJU0bhjD/yR1w==, tarball: file:projects/gmail-assets.tgz} + resolution: {integrity: sha512-38d2oBBbSU9BC1z3xI8zLvPOhfsEtLM1+D2GdNtf6US4zfz8unJNZxUvHLHOIwVwG4i1QylKWTda0XFbodUEXQ==, tarball: file:projects/gmail-assets.tgz} version: 0.0.0 '@rush-temp/gmail-resources@file:projects/gmail-resources.tgz': - resolution: {integrity: sha512-jNJe33EyOpp+1adG8E6wyJzbYaEEKNaU1OkF44Dd8mhUrKX4l7EcXaMAtcgFldpxj6ZBWvXsvZGilMlTlWjUEQ==, tarball: file:projects/gmail-resources.tgz} + resolution: {integrity: sha512-hEME2tvwXrmZcToezTWXk6Dz39RgmDJEr67vk7pBQOpDwNAzL/HsL4BJDmuTaenlSZNeNAZNbooZsKId7Zg+jA==, tarball: file:projects/gmail-resources.tgz} version: 0.0.0 '@rush-temp/gmail@file:projects/gmail.tgz': - resolution: {integrity: sha512-RTIwuWMQ61Rxm9OxjbY9qgt753e3sJxe52k72M8Pru1NjYBgSh91PH7BDAiqpGTmuA+vN5w6yYTwCnyOyrFpNw==, tarball: file:projects/gmail.tgz} + resolution: {integrity: sha512-yAuvWyvHxiJFmlQZgIM64vjAACXBxQ+56FiU9RRrZySf43uB7UusFPFjLLXJznVtebu396gvQVGKgP12Og9sGg==, tarball: file:projects/gmail.tgz} version: 0.0.0 '@rush-temp/guest-assets@file:projects/guest-assets.tgz': - resolution: {integrity: sha512-B+4/mU5XOwyDfxHTge1WuPJJbYcYtACgNRM0I3Z0iHpW9vTUPmqO0mUXuE8mfUO1a1FXCO9yRcr7StSpD+Zw1A==, tarball: file:projects/guest-assets.tgz} + resolution: {integrity: sha512-V8hsDmMYbIPsA/F4bmIA8E3QzmEIfDW1fF7B5MK0AXElcVMCD9fsMKYPcF9xv9zwMl9uYPqqgAwV3L/IaMg9XQ==, tarball: file:projects/guest-assets.tgz} version: 0.0.0 '@rush-temp/guest-resources@file:projects/guest-resources.tgz': - resolution: {integrity: sha512-YfPCDlvIO4bJmOvQg6RLFcWnMfn3olU2kzzDK5NRM9Szg4IIKtRtkn1eNKzcz+28Truz6Ky1xzuTmvoZIkKSJg==, tarball: file:projects/guest-resources.tgz} + resolution: {integrity: sha512-vAzsyTAqkIY/uEPIQFEXjmOr4gVC09VLkwccXDzmnvThT91qhADiX19tITKeApxvThUkta6o/wWRfGWYBPNLMw==, tarball: file:projects/guest-resources.tgz} version: 0.0.0 '@rush-temp/guest@file:projects/guest.tgz': - resolution: {integrity: sha512-Fdt6IZroKFKtnS51jD87RF3v7lC3WCOpEANFY4/JmLtZbcdYMW5TFpuYneWDmpW4vwXwiF2p+N6402+4gNFZ4w==, tarball: file:projects/guest.tgz} + resolution: {integrity: sha512-tZTMvqf/T0beZbVv02aul2ZRy2p1LjlXpqPiX2AnCe26TGKz5TrOvPWnyK5bc78jJS5ebO8heuwfsyI6+pbg1Q==, tarball: file:projects/guest.tgz} version: 0.0.0 '@rush-temp/highlight@file:projects/highlight.tgz': - resolution: {integrity: sha512-6yFEAPYgcyXvBO1xzO9ev/3o++kazqO/+J+qIbEW4MedjOdGiHu2ViYzlGtJGNNYdtypUKQ3858eHnucx1tqsg==, tarball: file:projects/highlight.tgz} + resolution: {integrity: sha512-53s036avTrD8qce/oSvfuhQMfUweI8dxVU+49BAinnJe8okpm+vEiYwvenku05Cwhqcf/+81OvqdWgD0AenwkA==, tarball: file:projects/highlight.tgz} version: 0.0.0 '@rush-temp/hls@file:projects/hls.tgz': - resolution: {integrity: sha512-y6P3JTdxWSEm6rM2G8xYVrMP+3vqJSQZhxIaRy3xjOx99ypXpt8ERE84/s3GWqSU+/jTKxXClDEW0ITIr3xHHw==, tarball: file:projects/hls.tgz} + resolution: {integrity: sha512-Qf0ScUhABYT+Bl5Z8mLB0Tw0D6tnRX2fRrmKUN/zQRrPM4suzarb67eSokh5QWgLG4MgyZ57t7VQYVrXx48+Mg==, tarball: file:projects/hls.tgz} version: 0.0.0 '@rush-temp/hr-assets@file:projects/hr-assets.tgz': - resolution: {integrity: sha512-ivgqHYuXIgFBAwM9q8sdYLsiKOcxUzcn/CU6bYA10bEscXTKe622Kg9ZsZ4zp+d8JEEyYmA3VSgWkjxUDv7Tdw==, tarball: file:projects/hr-assets.tgz} + resolution: {integrity: sha512-cPvM8WhebzcE+LSxiUwpJN++QhJDUzr215S4/T+NotJr+uSV7qyt5p5mY9Sjn6uuzHa/tlaFu9LaVcavno0ubw==, tarball: file:projects/hr-assets.tgz} version: 0.0.0 '@rush-temp/hr-resources@file:projects/hr-resources.tgz': - resolution: {integrity: sha512-tuXKKsvQ+f5CIFjEyWpk5Pgbvs5qKGvjXCtAm6h8n8L6g7vNW4T3nGf7PFD+f8/7DmZJdJW3/cNLU7XhmQiOaA==, tarball: file:projects/hr-resources.tgz} + resolution: {integrity: sha512-zCzPawSNiCDHqLQVnePEgvkLw8WiZzBZRR0uQd1mo1ZwhErIiZMHmtPuDhZr+iJMiq65sxb+x90fgT767OAnIA==, tarball: file:projects/hr-resources.tgz} version: 0.0.0 '@rush-temp/hr@file:projects/hr.tgz': - resolution: {integrity: sha512-JN1vthIEf20komgTicj1gwGjyYEnrdI1dEWdLcvG8ief2P5wgreF9sNPhjeYsqNJeV02Us0B+HV9vD98ZEUCbQ==, tarball: file:projects/hr.tgz} + resolution: {integrity: sha512-bXRVNWW67Ojq5LQsR96DURTvCgMZiAUkj6x9W4YFVRTI7tjMBIk78Pyd1Wek+T42pCWAf8FuzufXPRdXB/ygow==, tarball: file:projects/hr.tgz} version: 0.0.0 '@rush-temp/huly-mail-assets@file:projects/huly-mail-assets.tgz': - resolution: {integrity: sha512-HZSuek3wpfjV90twm5KTwdsBkCF7ZpZO7oRWG1cro5kFPVNUQCBdYb5coj7eMHMBrGhS8D0D5/8lBnrNWYakjw==, tarball: file:projects/huly-mail-assets.tgz} + resolution: {integrity: sha512-G/ajPQ5JfsrQ3ZdcTbeAlzQripUpKzsPyP6RHdM6MNjXujIF0pIdscKjqpjNkmstcahBhTnCxb29eTOFzIQlug==, tarball: file:projects/huly-mail-assets.tgz} version: 0.0.0 '@rush-temp/huly-mail-resources@file:projects/huly-mail-resources.tgz': - resolution: {integrity: sha512-PD6flmlLUQ4bibKwO75CdM64xjlLaKtsoqb4Iy7pAjvWy9h0TRGo/MU1knRR275Y4sPNkyiEwxPYlnUcicS5eg==, tarball: file:projects/huly-mail-resources.tgz} + resolution: {integrity: sha512-wHLxctcqO0Sk6aadmMYIWY0HGVCfAY1aFvOzbziUFvslwz/V1FrTKPoE/NCe1m1LzOpCBu7qEqDMf3M4PhlNsg==, tarball: file:projects/huly-mail-resources.tgz} version: 0.0.0 '@rush-temp/huly-mail@file:projects/huly-mail.tgz': - resolution: {integrity: sha512-qIQ30Wg0m+hWMfmJdB70nMRMBcstQmoax9E42wBMeyioNgVJ6VPI/i15l4lOIgPqD7ovY5ZPFHibuOIexxg0Lw==, tarball: file:projects/huly-mail.tgz} + resolution: {integrity: sha512-pwWZHNFKI0gn9PupT9kgbzxBfJODfwpWKXz6nLHZJ7RwyLdB8kzWRs9x6/vewFwbactI029JsFWqjKNwyQyiFg==, tarball: file:projects/huly-mail.tgz} version: 0.0.0 '@rush-temp/hulypulse-client@file:projects/hulypulse-client.tgz': - resolution: {integrity: sha512-ioHzzRYwOSFLo3Gxfp8GU8xPXG3WQbH+CvnKRvyNSMq5rzRJdTH8ybXoIfX5/dXegm+oItOKr5bXn3dF+I4DKA==, tarball: file:projects/hulypulse-client.tgz} + resolution: {integrity: sha512-WMFQOoJ9e2NWMYiRYlJqVqnYgakC0RffYoWui6TIPvE1vOHym1BUpAtfwKsBIavdhWHrdLfejLCEoAI/qM2H2g==, tarball: file:projects/hulypulse-client.tgz} version: 0.0.0 '@rush-temp/image-cropper-resources@file:projects/image-cropper-resources.tgz': - resolution: {integrity: sha512-MriW8LIFqa2dD2ABePbXuP7XHpNPKpQjrkxQvFHhXqpqnkZKhmI1nmAhhDftSP8iPpsKB71wAAwP15y/nas1lA==, tarball: file:projects/image-cropper-resources.tgz} + resolution: {integrity: sha512-mTNc+dzx3s/jMbNOpkwJg3EOe5KbyBT9v4A2luUkIpn8XVEzn7OjA5g7p0CnphtRZ5sG4Q1vaQzrBo3Nonw0GA==, tarball: file:projects/image-cropper-resources.tgz} version: 0.0.0 '@rush-temp/image-cropper@file:projects/image-cropper.tgz': - resolution: {integrity: sha512-l318gg7iqrqXjdc8FwPuslOtRXtD2mhf7Z7uG7RvBTP7MrZKcvBAL+a/rUeKzCi+NeyjyZbbgOl06un/vu+iyA==, tarball: file:projects/image-cropper.tgz} + resolution: {integrity: sha512-CNNZ+jEGTs1l0ABXL1PEiWuzZCrE+oBMk+TLgmKvgkuakOn15/q6uDWio/LRRJYTbBflAxN/RJDTqY1dxodGZQ==, tarball: file:projects/image-cropper.tgz} version: 0.0.0 '@rush-temp/import-tool@file:projects/import-tool.tgz': - resolution: {integrity: sha512-GX8ghRLyEcK5wFJKnMIrjf/RfOhFbtk4J4eV2P39BKX1fiWlOIiI3t3YywgKK41fG1bcW9DCNCKOCiZVLLZ/LA==, tarball: file:projects/import-tool.tgz} + resolution: {integrity: sha512-fJkfIXTCm3YAMW3b+ZP4XmVeViaHkxeR2la8Av6YrNBN0MmE/e/XSMDz29cVCzz/ss4DLl3xYsAU8GUNERwVEA==, tarball: file:projects/import-tool.tgz} version: 0.0.0 '@rush-temp/importer@file:projects/importer.tgz': - resolution: {integrity: sha512-HF/emiiZFsIop/gjEe89wCsYiOK0jW/mpG370Clg1zvfJPVdx4tOXx0wMYyyYXgBtbxuff1dxMl0FYkGTgEFWA==, tarball: file:projects/importer.tgz} + resolution: {integrity: sha512-0ZTIGkEdN2wH3kkQY0+1CO/MW7JKuzVNKqgcvFv1c4WNBUV8GApQPTS8lhMAodRH7HY/PTSoYMWvBiOJYQCJJg==, tarball: file:projects/importer.tgz} version: 0.0.0 '@rush-temp/inbox-assets@file:projects/inbox-assets.tgz': - resolution: {integrity: sha512-7/pm+2K6UWkYKB9J44WWQosGsB6d3/2GUW9oNBr59qf9GkKoKVVk4AjgJmy7f0VK02Vja4RhJIakgCOAp1VKtg==, tarball: file:projects/inbox-assets.tgz} + resolution: {integrity: sha512-/K4hsN9ZLM6L20EgcDtFCvsqtjlWguJKQPyQZNxWOgddnYMnpuRpEW9lI1KLHKandrIN0J89o9pBv8KAW82AOw==, tarball: file:projects/inbox-assets.tgz} version: 0.0.0 '@rush-temp/inbox-resources@file:projects/inbox-resources.tgz': - resolution: {integrity: sha512-QyiZubyN9SUV5rRLV0fGCT3uMhBYwlgN3moiEDSF7nNl3fnLERChKSBFBh+yjXYi54U/qeg3SK7s7nySt7GXbA==, tarball: file:projects/inbox-resources.tgz} + resolution: {integrity: sha512-aLmKuC29WiVDOTOkJQyZn3PTBC7zp+6yMmncoWhCqmeGwNC5GqTMfXZLoCPSoambYySHhfG5BpssSypdi8flzQ==, tarball: file:projects/inbox-resources.tgz} version: 0.0.0 '@rush-temp/inbox@file:projects/inbox.tgz': - resolution: {integrity: sha512-22R4fC9ubq/ibEPgd2oktsehfdjJhS6Syx94AB6Sl/jz+a8jh+ImRPUcvR/fOFnqYAfcsGpu2uyNIsap91rDhQ==, tarball: file:projects/inbox.tgz} + resolution: {integrity: sha512-23LWd1vA+rLpOykwsjJd2bEZYujHopIG+8wTPh7jmx4Dqh8Om/ib6Dp6V48VSrg/TzDWZ7ynJLZioaZ5oQKLFw==, tarball: file:projects/inbox.tgz} version: 0.0.0 '@rush-temp/integration-client@file:projects/integration-client.tgz': - resolution: {integrity: sha512-P4Nwj3lpDdwxp2ZPtauTmhMyByt/ttGc6kk+ezo2t+vOVas2v9fbiZgU0hgrLPDLQ/d7s1fnbPDi+ThN60SNQg==, tarball: file:projects/integration-client.tgz} + resolution: {integrity: sha512-MUBfZ7mNTOvyss6k8CEDDhGepFbwNBDnGyqSlhy6agNl4AgS6ndtewT5GcGkMgunt+AGsTBtCr0JwHKCF7XynQ==, tarball: file:projects/integration-client.tgz} version: 0.0.0 '@rush-temp/inventory-assets@file:projects/inventory-assets.tgz': - resolution: {integrity: sha512-lBo8ISs8EZGDZaMJ3OHSpHfAt2yYaSVibhLPGY3GABrsCIKWhXDczaJV61xfkZhCY+Pdf3on+y4o7KPg8eZq9g==, tarball: file:projects/inventory-assets.tgz} + resolution: {integrity: sha512-GsrQBvXrcBpNuhy8bnNnjpU1WIqtpcy+qA68HZl9Bh1sV/86mm9gUR8dkzgt/m0s6mCeU+He/oKYdbWBHyzg7w==, tarball: file:projects/inventory-assets.tgz} version: 0.0.0 '@rush-temp/inventory-resources@file:projects/inventory-resources.tgz': - resolution: {integrity: sha512-HmVA20dKZF7LyHzyKQOBZnqAqvUtZhiKjoYS9SFYVtqY9joqJcG+Q4kRxtaR/ylxSVfWHP6fF+Oj3BfOoiQ07A==, tarball: file:projects/inventory-resources.tgz} + resolution: {integrity: sha512-kNmD5RRHFbIg/o1omdXSXPm4Z9BvrdEuUL2VVauEwLcgMBwjzfYYxpO13MXEycuiwH0NuVLj93++6GjNNNUojg==, tarball: file:projects/inventory-resources.tgz} version: 0.0.0 '@rush-temp/inventory@file:projects/inventory.tgz': - resolution: {integrity: sha512-WIgMQj3gAsPeMAscJaZKvmIRNZ7Tqx3EnaDJfeV5A/YYsx6yYy8Sz5sExxjF6EDyL/nCx0DwKTjKPkAE+mb6Rw==, tarball: file:projects/inventory.tgz} + resolution: {integrity: sha512-t0FUACgqmwcCEHtuJI23G7syQgLLXKV24bbce0/BAIx+zeBdUFMyGWU22WVjYRo6U0vg1rJ0mg/FKSJj4+pDBg==, tarball: file:projects/inventory.tgz} version: 0.0.0 '@rush-temp/kanban@file:projects/kanban.tgz': - resolution: {integrity: sha512-gut/vEF7Jff/KpMBzO3mSqS78FEGDZIvk6EIhiQRUfJ7gf+Pk+0DIl5KvRzRIDW9b9hx8GJGNvSNgiLtQi1fgg==, tarball: file:projects/kanban.tgz} + resolution: {integrity: sha512-9FlGzasWkzvAYnfssmC2z6tDTmyEV1uJACB6t+GICQQUnauw5/Xeafiac0Ztmn+Y7RfKP5doKt/ehh4qDn3KCw==, tarball: file:projects/kanban.tgz} version: 0.0.0 '@rush-temp/kvs-client@file:projects/kvs-client.tgz': - resolution: {integrity: sha512-fvpAsK07BSkLS7zpeEuxGRKxinAMV0VQsvn4cvJdJ15p6AOalC4CqkLmsuwRV1Zglg1h4ncVfsR87xXNArdUdw==, tarball: file:projects/kvs-client.tgz} + resolution: {integrity: sha512-O+1wllqX/M7ob2pYAwjnWbCN5peRjZE1HUhDmUAignBOkG1RkjFGon/9Gp/ywJ7/xIMseNylQVIknHQq1xQn8A==, tarball: file:projects/kvs-client.tgz} version: 0.0.0 '@rush-temp/lead-assets@file:projects/lead-assets.tgz': - resolution: {integrity: sha512-KUkefoq/RCqa4O605V62zkShVpbWSX3SsDMvi3Aw1FBRxyveMg0jMMLBOBOJKVRzteofFq27ZjHd+hnLfS205w==, tarball: file:projects/lead-assets.tgz} + resolution: {integrity: sha512-cyrJkrBBuGa148N0SmD/KC7a/roFY+USCTautENKWH1O+8CR1ez8EuQh9+47gWdmqnSdSVnHDjwFJ/8lnFzLUQ==, tarball: file:projects/lead-assets.tgz} version: 0.0.0 '@rush-temp/lead-resources@file:projects/lead-resources.tgz': - resolution: {integrity: sha512-4R4LgzJa5xOmBugZW7YSgHgFPk2oCgQm5fjZjq6aehEMfyZ6Pw4ynTCCkxQPkuD13WQ4JWuQ2XPHQ6jm3SdqtA==, tarball: file:projects/lead-resources.tgz} + resolution: {integrity: sha512-loF3txcxhYT8mYs4UwjcaueYkhtgyBluNH2Q7cYimZ3TnnW3sTeYDzYQhe586DvHxnltbT+FpI5/IkcqT29RWg==, tarball: file:projects/lead-resources.tgz} version: 0.0.0 '@rush-temp/lead@file:projects/lead.tgz': - resolution: {integrity: sha512-/OfLSNFU5P/u29vW0/BDT/tAy3D0gmekra12BXyXUay7YVPtheevP7xfcZjxITqOIIYNjOmgB+F9tNvBXy+YpQ==, tarball: file:projects/lead.tgz} + resolution: {integrity: sha512-kX51mCmeTBDTCwLzeuug+rRv2Qc1OY7gUnGTOI/J+paoyN7RC/uG1zv8Evta55oCskB5+s0T28UWLsv/iZwM/g==, tarball: file:projects/lead.tgz} version: 0.0.0 '@rush-temp/login-assets@file:projects/login-assets.tgz': - resolution: {integrity: sha512-mg4deqkkFmrmpA3KFQAMPR0pVpjNsCvCYwb6wkimjXUf40kMzNYuOlOrVIi3M1GxrKkztG2JefBuMWVy0924sQ==, tarball: file:projects/login-assets.tgz} + resolution: {integrity: sha512-UFcZFjW0+1YwQG0IX+YRNu6v8j8ZsSWw5U3j97csIJkMK7g+5qAY4NVSVPjbUa4S3iuE/qXm6yQhO6XRTH/J0g==, tarball: file:projects/login-assets.tgz} version: 0.0.0 '@rush-temp/login-resources@file:projects/login-resources.tgz': - resolution: {integrity: sha512-t3zre5+uGYJjHtAIGIHrM+rr81zqdnLh1rXVOgRAcGdD0oKADlsgA6TpqweN3X0FWUyxJS5cs61NDF2TxMvviA==, tarball: file:projects/login-resources.tgz} + resolution: {integrity: sha512-CMQCn/Nx8g6umxJVOzxV8BmrYl4Q6JCwbWqQYP09vSfZaXeJ7ZnL1Vnu1aQRyltODI1motowfZPOsq8za5wX3g==, tarball: file:projects/login-resources.tgz} version: 0.0.0 '@rush-temp/login@file:projects/login.tgz': - resolution: {integrity: sha512-WeIdaszwOQWZP+OaqOQLpWOl5/qQQIAdKCO9tmNRJ46VExbcdK2ne2la5hejoAaFtcnHKRHV58M6FnRyBfOtxg==, tarball: file:projects/login.tgz} + resolution: {integrity: sha512-/8SZdiojLG6UAgSdNZtPursqqzEA1eY2KDBQk/931SwUoxL0WS81mu/XjGvdSszAVbKgQbwLrMXbnLfJUyaDVA==, tarball: file:projects/login.tgz} version: 0.0.0 '@rush-temp/love-assets@file:projects/love-assets.tgz': - resolution: {integrity: sha512-jwpugKA2YcvXQWoUzW1988C6IQjgSbiLPVp+/ntN9SnZRENaxp455oNkDIK5VCDGjfTxeEfDd6MgMJMsmCJOdA==, tarball: file:projects/love-assets.tgz} + resolution: {integrity: sha512-/N6XQYOpTc/4OYUqO6kwby9bK6wK+HGn6Zp4LpqSDcFcLU4XLY9nwkrRhKM9qD3f2SswruTWHObpLHVIFVo1/A==, tarball: file:projects/love-assets.tgz} version: 0.0.0 '@rush-temp/love-resources@file:projects/love-resources.tgz': - resolution: {integrity: sha512-kRhA3lvEsi7tXskg5ux0cfDCBWCPjsgh4pkUsE3n0Him+QKxHgEqPBDGUEc8meqWrbUA4GCAVOfNY8D/Qw5CCQ==, tarball: file:projects/love-resources.tgz} + resolution: {integrity: sha512-7dkaiqP6tBxF2WmdwGLOZ97OjdxZz6xP2iBJLb6L48rmQ/7sB+CIqYlRmzfFDgJLFwF49QUwl0KCkMkyXThIWg==, tarball: file:projects/love-resources.tgz} version: 0.0.0 '@rush-temp/love@file:projects/love.tgz': - resolution: {integrity: sha512-P2jbM7A8TNzjWd4WoYnDqA6eNvK6WmEhU2nQsNVb0Z8MsPw5y1SbRjFHTnzdET9ikL1xYQ2Nunq+xDbnMpfdDw==, tarball: file:projects/love.tgz} + resolution: {integrity: sha512-RRVicDMFd2n+361k3T7VJ25l5rqyA9Z97939dk9vn/qjWynvY03JfxozOmWrbc9X4fmo9ds2K7a0myKTv+iZTA==, tarball: file:projects/love.tgz} version: 0.0.0 '@rush-temp/mail-assets@file:projects/mail-assets.tgz': - resolution: {integrity: sha512-3GGbOsFjxv6Um4uHCG7Rxj3mnwoL+un826RCLsOEgDSkReI2rJX8MzhWfc8qaXImlyKwADgmNNyZzbt11jvFoQ==, tarball: file:projects/mail-assets.tgz} + resolution: {integrity: sha512-l2Zj1LVpmUxc6U+byWSB1+hZEJn+v/HrKQtA3ALnNBlv24j7AwRMAm1t9NgM1DSlZdyF/0jWNoNWejAaS4RYSQ==, tarball: file:projects/mail-assets.tgz} version: 0.0.0 '@rush-temp/mail-common@file:projects/mail-common.tgz': - resolution: {integrity: sha512-FWSZ9r86MB3jZeYzCzWBvsMoOKIhezFYOSW73Tc9O0A7KmaCZmeh7FK04GMtdbRUY90eh9TpZAH2s03t96YECQ==, tarball: file:projects/mail-common.tgz} + resolution: {integrity: sha512-fgthKDS3EJ6whqPuVTaKRz9ZCXd5YJGjQvlhBPdqvRIng6H2hWOiFgKjzyoirxqjW06csQlymPlKNtyaqdsqiA==, tarball: file:projects/mail-common.tgz} version: 0.0.0 '@rush-temp/mail@file:projects/mail.tgz': - resolution: {integrity: sha512-Wkr2LXVJpfecuOK3B6CsZtxRZNuOobYEDxHJtBDyV7Kp2jtEC/NjjkBJYHM4rOw3y09CHUtUVxz3IWjxF6sYqQ==, tarball: file:projects/mail.tgz} + resolution: {integrity: sha512-6jb+8WiNxTAKO47TUqXq9WsEu7Xl7bOfSqGTT7ZfwA//kZuk80lpOZdhPnnokqc4z/sMjC9BULDl2W6shadPhA==, tarball: file:projects/mail.tgz} version: 0.0.0 '@rush-temp/media-assets@file:projects/media-assets.tgz': - resolution: {integrity: sha512-YPYc/3OhLeqWIa0DKzy8ToBoN/isyVssiTFh67oIksCuoGGp9Sf1QC+/ZQ4cmah05Vd3bHAh61Xszy0hpiEzuA==, tarball: file:projects/media-assets.tgz} + resolution: {integrity: sha512-A1BKGgpqmJvSbBaYo5755xiDtKWzp0dXRcmsfaKbs8JbEtQDBMF9MKVZOp672wJe3un9ARtrUZfGu4LsjPrzlg==, tarball: file:projects/media-assets.tgz} version: 0.0.0 '@rush-temp/media-resources@file:projects/media-resources.tgz': - resolution: {integrity: sha512-bZv66lXG1pqWcAmInzn0wPcHYe3L7RQvgYzYtyLW218vhlMypb8tyaJSYfOIhNmMfLcf77706tCOrmzVZfcqqA==, tarball: file:projects/media-resources.tgz} + resolution: {integrity: sha512-90NFkH5PgAakiZ2pj/IaY7+pOgdc31KES/6SLJ7gmge6lY0ID26fR7MUr9Ei2Qv7tL/bunxo93EBdlfP04x5Aw==, tarball: file:projects/media-resources.tgz} version: 0.0.0 '@rush-temp/media@file:projects/media.tgz': - resolution: {integrity: sha512-A1q8SiC/4M5SFrLs7lh/ayeD0gGC1D+ezv/OGIZMxt5HwSQPCoW1ASRJqlE78DY4steGJFqYgO3bASzUL1fU0Q==, tarball: file:projects/media.tgz} + resolution: {integrity: sha512-JSPdbyNW4SQuxMLCvSUN+bdq3dommB8Clhh9naesA3x2UXt8xbGV3XhToQb7LmWKTI11SRRa6ywgK0lkQ8qhzQ==, tarball: file:projects/media.tgz} version: 0.0.0 '@rush-temp/model-achievement@file:projects/model-achievement.tgz': - resolution: {integrity: sha512-eo9JGRF/tuTh/9nsb/eKX6iUelMHoR45cHxdsZuVUBFurjN/ROcBaHMwi5av/5xdqPi/ZslBkdmL38lLAM0t+w==, tarball: file:projects/model-achievement.tgz} + resolution: {integrity: sha512-Nt+lkmDWIXDApYXTISGHe4OBB+fkIa6B4cBMAXK5QrgHLmqPKu7OEwaMtFbIPSD7gwTJgvnxg0rKV6WwIs4ilQ==, tarball: file:projects/model-achievement.tgz} version: 0.0.0 '@rush-temp/model-activity@file:projects/model-activity.tgz': - resolution: {integrity: sha512-ctcCLOxxjMjEZRFznjjyjPpFVL8D9GxAqvp955MeeEitP/UnU1LgYaO0gW8tRN3OxA7qfz5M6CCLQ1QB150Cfg==, tarball: file:projects/model-activity.tgz} + resolution: {integrity: sha512-Kqri+1YsmtKgfGZ0OlMxbP58qzrsLotaAdH/+cEknIApqNgPLNom0zp+5Kaps71b1Azj4nEthiFjHK7E3s9cXg==, tarball: file:projects/model-activity.tgz} version: 0.0.0 '@rush-temp/model-ai-assistant@file:projects/model-ai-assistant.tgz': - resolution: {integrity: sha512-D1Rpr65uYgqBHFiTIek+UfnIjKfHpOZR23iaXs/Qg0TsYb/+5Uefl7612cnFfKiXSYx6FDey26zABgFk2Vu0vA==, tarball: file:projects/model-ai-assistant.tgz} + resolution: {integrity: sha512-/7LpSYlRzgOmvW0Qn+KxjJ6Pfv4hmMSjubgRciPv8WoNma4VThk2JN09LQAv/8mHhvTuPYBdRaEm+AWp8r5U1Q==, tarball: file:projects/model-ai-assistant.tgz} version: 0.0.0 '@rush-temp/model-ai-bot@file:projects/model-ai-bot.tgz': - resolution: {integrity: sha512-xTEinPR4/O9ZQ3TbDs8G5fVvqnT4PTqUg9kdL9tlCakc3xWXdBfvMPkjlKwnYliQfT7zx8is0KQ32bqv5SvACw==, tarball: file:projects/model-ai-bot.tgz} + resolution: {integrity: sha512-vrzG4uOsTCC9dhbJOi+bsP1TDbZZwUvSkPYd2dXYcoALB3hJvDLZvUV9SeZlyPcNmZ5LJuI4Mxe090S50yov6A==, tarball: file:projects/model-ai-bot.tgz} version: 0.0.0 '@rush-temp/model-all@file:projects/model-all.tgz': - resolution: {integrity: sha512-EOF5LrpYzOmiSszBnzhXQIsKzRL2ZOffs5sG9MnELZiOrtJjcVQ7vOYs4M8mbJwwTaz2ZbItEj9K4RbTCD9vyQ==, tarball: file:projects/model-all.tgz} + resolution: {integrity: sha512-8zMPzAqQnVCLaxMJV2aHAXHmLGXc/mHe80iaNiamT3fmtc0E3Hb38UB3q3M7+jxfoIOVm7YxKUXvhEKrxnte1A==, tarball: file:projects/model-all.tgz} version: 0.0.0 '@rush-temp/model-analytics-collector@file:projects/model-analytics-collector.tgz': - resolution: {integrity: sha512-oEW43wD5bwJXYhmNnikvzjs1SbnfEZen1kFBRIlDdmIKl4VYy2qMmtDvDLOiKiUWhH8G9XDcfT3dq7ZiT+1yHA==, tarball: file:projects/model-analytics-collector.tgz} + resolution: {integrity: sha512-mgST6CkVsFfzflFHFij/FG6ZyRG9xtjwFeqOwS1JKALlVonrrCLUX5NL5YuApEgo11MNcsVMfhSiSEBKqIKUeg==, tarball: file:projects/model-analytics-collector.tgz} version: 0.0.0 '@rush-temp/model-attachment@file:projects/model-attachment.tgz': - resolution: {integrity: sha512-xtYMTvC6fnwGsG3aa9xhSazT55UwIlW8eFpWP/FSCdvlDQk0JLWswt+tJsdJdamR8uE9r+1IbPTmTYvDESMUTA==, tarball: file:projects/model-attachment.tgz} + resolution: {integrity: sha512-hcxdJc6eXwmEoNaIYspys3TylE9ovdiVTWGjXInENxZGvGTmzCnwS5AMJ/qIDZ+dL8JEZWP0/0iz3gS1HTs10g==, tarball: file:projects/model-attachment.tgz} version: 0.0.0 '@rush-temp/model-billing@file:projects/model-billing.tgz': - resolution: {integrity: sha512-Qykz8H1jPAR5UeunI50Rl4s49VQFQAm4Vi02lddn4sBAer+eJLjk2u4FL5shhsrLo5rhM8rJlmItL1VWQcb+RA==, tarball: file:projects/model-billing.tgz} + resolution: {integrity: sha512-3kvXvy/BXcQ/w6UJYiP8Qat7tY9kCbxcvKoWhVFVGhyeuabtVLU1PdLrrrWYh13nLjPd4bf+ZXtPKQbePO7QCQ==, tarball: file:projects/model-billing.tgz} version: 0.0.0 '@rush-temp/model-bitrix@file:projects/model-bitrix.tgz': - resolution: {integrity: sha512-Rsw6Ia4eIAyCi/YnZFuGB45KOBPHHVH673KmA0JoFMnPsJ0VOLPnH3tgowjl6QJm0BySJ4BPHRv+HqmLQaBSYg==, tarball: file:projects/model-bitrix.tgz} + resolution: {integrity: sha512-9okJSluuphs8DhKPLoIU9yXyg5aeRAo9pPok4AZkhI4vVjE6xfPbeqFZ478h1aoZ99bXbGmTZeinF6Da/lYSrA==, tarball: file:projects/model-bitrix.tgz} version: 0.0.0 '@rush-temp/model-board@file:projects/model-board.tgz': - resolution: {integrity: sha512-8xt9KdmNKjwsv5leK367GYSyTxlMBFU4301BnbXE4dYNu1FE+s2C9vzXWsgHpXnXC3tenQaJJZtRragliM9zkA==, tarball: file:projects/model-board.tgz} + resolution: {integrity: sha512-sVQtc9q4td5Ag7i6d2yIM4kYwF93OkhivwoejbRadKUIQfepVqUnkh22IH3aA8hVSTulzbJoAf+mjf3Xpr6n5w==, tarball: file:projects/model-board.tgz} version: 0.0.0 '@rush-temp/model-calendar@file:projects/model-calendar.tgz': - resolution: {integrity: sha512-V9aQQZYfuasCWdDwMHGL708o2A1JMGi875whiZz4ZwMzWTNV+B2LiKyNYuuXz/1fMS5drqplN0qnB296oXccRA==, tarball: file:projects/model-calendar.tgz} + resolution: {integrity: sha512-pZYkiEMijCJq7aUas5FcfUry6LIzXoKcrKOn2YuG7wHftZ3c6kifM1FTYdfmmWahEsZkYTaJ1Y8wkooxUNtsyQ==, tarball: file:projects/model-calendar.tgz} version: 0.0.0 '@rush-temp/model-card@file:projects/model-card.tgz': - resolution: {integrity: sha512-YicV+kafulEdmWfwPr5QEfKYilIwKQ1wWppp6tpY9z+B9oAILNFAvz0l/GhxW3Ou7Ms2KkMrz+SRc/B1RV5bQg==, tarball: file:projects/model-card.tgz} + resolution: {integrity: sha512-1ioZb5gvhoVnIKdjnIZQYeO6EV5qDHmknE2zqD80bTAgkuAe54vWz5IFBV/iwggWWIvlcoEnLgxIzfkUbdtw2Q==, tarball: file:projects/model-card.tgz} version: 0.0.0 '@rush-temp/model-chat@file:projects/model-chat.tgz': - resolution: {integrity: sha512-R5ocOUqYdeIUzZmwaggWPLQk+vLCWVg6H2EN43ctNydWqnidBloXc21GgjfRqaWXzmYDhNKuZkJvxXglGEcudg==, tarball: file:projects/model-chat.tgz} + resolution: {integrity: sha512-duSEctEAOGIGE+U/lftFn5GubLoVpLDO/4W98r8IsiYxJJx7d+b4QHkUj6s/OOfYyQADPxsDuDp31L3NwgcqoQ==, tarball: file:projects/model-chat.tgz} version: 0.0.0 '@rush-temp/model-chunter@file:projects/model-chunter.tgz': - resolution: {integrity: sha512-n/cA8QEz73XJk8EqYzV3q2x9pQVhKR285tNDVCa1g4tdpSeL84UQd6RuQs8Q6BQvH2dV1YAJ0MMw8B2x0JQujw==, tarball: file:projects/model-chunter.tgz} + resolution: {integrity: sha512-yxCZkZS+jx8CMOZyLYjZZK1zw6Rd0TjxkvaMARzRmJKUCh9WOg/Lkd5WixSb3l002EzzIot79H+B9dZ+KnWc3w==, tarball: file:projects/model-chunter.tgz} version: 0.0.0 '@rush-temp/model-communication@file:projects/model-communication.tgz': - resolution: {integrity: sha512-xKD2rHUJ0gDCndqYivxOxwKyKUzsV7079StbVH0RKTQsIOBq/9x8erBXwInSZI3ovBFcw5MT05KtYNEI4sNQbg==, tarball: file:projects/model-communication.tgz} + resolution: {integrity: sha512-nyKcMDN9wzeJwa4VTZAZkZrqH2ABC080FU9vOe+uzTKc5h3KbWQjclbCCL3q02cMIVG/0Djx1SaVdYVj4VQhsw==, tarball: file:projects/model-communication.tgz} version: 0.0.0 '@rush-temp/model-contact@file:projects/model-contact.tgz': - resolution: {integrity: sha512-F3yGB2ItBZTrnP9gO9JUf5pzB3uqfdR+R9Nalj+oRsByl/4+WbTGp1ahnxaCpywJuWnSSAOfz1lh7gta/mByQg==, tarball: file:projects/model-contact.tgz} + resolution: {integrity: sha512-kvA/3oY+Y91zT2+3vBqOtVjL4LEJA4dNenZ2ctt9g0zwtqFCyC2eHxEBL3OAL3zQai2AU0BAgewMZ2s8d+qGcw==, tarball: file:projects/model-contact.tgz} version: 0.0.0 '@rush-temp/model-controlled-documents@file:projects/model-controlled-documents.tgz': - resolution: {integrity: sha512-O9IQ/fJcKPZfQaGRv74Al9ypiWSjGSBLozqv6KAEG1Nv6FCBF6jwPveYY4Q73ehrWql1KTbgrjMPp7rgU/82PA==, tarball: file:projects/model-controlled-documents.tgz} + resolution: {integrity: sha512-1ct/gKLKBewfRN4zsLf0P9HaHkSa6Eucs3R5GD2u9RC1nKZShPIJO4DJjMhARmsUEnx72Lw/ZFuR0dvl8ZKm1g==, tarball: file:projects/model-controlled-documents.tgz} version: 0.0.0 '@rush-temp/model-core@file:projects/model-core.tgz': - resolution: {integrity: sha512-vsvqbwgx6M4Ba5Va2VZAEl+amPQ3KPdf0jJex+iWmaB31wDfpjOzKAM6Dvccj6FvOJNMrVWx6P7XtjMsup18xA==, tarball: file:projects/model-core.tgz} + resolution: {integrity: sha512-5HjarSRtuFEEqUgi73u96eo71GHauy7z4AspEmCZPd2gDHwjySzntKjc35QVVF/nlVyxm0PApQJNRzRsha+j/Q==, tarball: file:projects/model-core.tgz} version: 0.0.0 '@rush-temp/model-desktop-downloads@file:projects/model-desktop-downloads.tgz': - resolution: {integrity: sha512-9jkQi10h//ubUq5X24owru6jcj23aj1NLCFXI+D2etp9pwyRnJ6+YGNo0lcqq4nS1fFrXUC089iVYQrYnIEBIg==, tarball: file:projects/model-desktop-downloads.tgz} + resolution: {integrity: sha512-8CCT+PoZT83E9XjNZkG6N/fEwT4QW8mfrVu9dusuo+KNff+hPkdBDXozR2lKJHq+azFbGLRHjfrCQUNIv/VxKQ==, tarball: file:projects/model-desktop-downloads.tgz} version: 0.0.0 '@rush-temp/model-desktop-preferences@file:projects/model-desktop-preferences.tgz': - resolution: {integrity: sha512-ZKGK+ld2j4LvBb0k7uTe5oPcA9jdXH/957YB5xnth0eacO9MwZqIrS2hXMNpl6WmRA09VwKmYZdYqvxedMWB1g==, tarball: file:projects/model-desktop-preferences.tgz} + resolution: {integrity: sha512-vaJVlGCEN+zcSZRCIXXbUu5VVO4cfTxS3IOd8PK07VF/9OPatBK2iu0O6gnDz34cw+Ernpn3FeMzYhkFB/+7IQ==, tarball: file:projects/model-desktop-preferences.tgz} version: 0.0.0 '@rush-temp/model-document@file:projects/model-document.tgz': - resolution: {integrity: sha512-39fYqmrodqWXte3aeAkwOisS6bhX+hFRHxLMWD2CTZMuPoIXwSWbc1CSrAYMfoAGkVEpxslkCQkqC6Gb25aVNg==, tarball: file:projects/model-document.tgz} + resolution: {integrity: sha512-zDkWiK3JLrRB4gXi11OW3a4uYIeulGhKEuUN+VKyJXXyvBAreTqcoBMhqLpynabNjMV27Lez0LmgC4YhliF9pQ==, tarball: file:projects/model-document.tgz} version: 0.0.0 '@rush-temp/model-drive@file:projects/model-drive.tgz': - resolution: {integrity: sha512-N3VlTbypK25JrVWQXCZDWfgryVtzA2HuLEGXZdCoNnRYLtoeUZ9M3k7khPeM7q5wW84CUn1S7C9U7hV84yGPdg==, tarball: file:projects/model-drive.tgz} + resolution: {integrity: sha512-bV50piKSc7Zahb6GOWonPxHsBM7TppMCtner3T62xFy3H2feeMZJSQMIgoq5BoeClL3WDu8L9gtLcxuAi/oyHQ==, tarball: file:projects/model-drive.tgz} version: 0.0.0 '@rush-temp/model-emoji@file:projects/model-emoji.tgz': - resolution: {integrity: sha512-0iM0fJKANeoIZ6+g0VoHG02gbrr9H1K0zGbhTl+Nw3W0X3vmXG1WKMIZKnaiab8PdZ1z99JfeTUNpvzJ3BrH/Q==, tarball: file:projects/model-emoji.tgz} + resolution: {integrity: sha512-C0MYzNUZJkxYy0jiAUdv0D1vsgnZrs+JIf5WoWQepdEoj2yLzZYFtb/eHWL/ckfuRx89Anel1uSKpRPj3dxz0g==, tarball: file:projects/model-emoji.tgz} version: 0.0.0 '@rush-temp/model-export@file:projects/model-export.tgz': - resolution: {integrity: sha512-+89CSIlJvt1i1SUgSJJSIx3ynppBZVp+8lVSJUrxwRKs57Z1u3p3JSKrBF95aMcHLkQw0urI6vCw+kxuNDd9wA==, tarball: file:projects/model-export.tgz} + resolution: {integrity: sha512-LWUqnimmrnxjHI+XFhrFDgIzugbOrOd9eJ/MNxLYNgGOSdp6cmrlT+5lyvqUm02BGwKPkw78pKuqA8MR7N9n5A==, tarball: file:projects/model-export.tgz} version: 0.0.0 '@rush-temp/model-github@file:projects/model-github.tgz': - resolution: {integrity: sha512-4Rn4vp1ir6m/uSFqnkHADEiDXFt40JxF/xu4pf+Hs5lq7TsxrEtKq5pg7VSa/ZvPLRhG5zSqTRlyBO0+p2HAjw==, tarball: file:projects/model-github.tgz} + resolution: {integrity: sha512-ajOGVnlBh1QE7uHN4RzTOyeaALkUSKi7VGq2ZXm/tFLSqSQrPF0waCbfzbLvbKdtm2mDiYfxc/uEM/klSQNeuA==, tarball: file:projects/model-github.tgz} version: 0.0.0 '@rush-temp/model-gmail@file:projects/model-gmail.tgz': - resolution: {integrity: sha512-mSnJ8X7lIJJ2ctDD9zV38RtAevxBfLr9PnQCkA9XcV7+dD5G8meb54y18cmdyDhk2I/EufjrnZn+tyUecT+62w==, tarball: file:projects/model-gmail.tgz} + resolution: {integrity: sha512-aRYDPypNqS+nBfhCDT9h8FVqzs+4tbs8lFD5Fw3rTqwRwd+TC4YW7GGoHWlvnaPjk/TuQWFxjqCut6H5W0kNzg==, tarball: file:projects/model-gmail.tgz} version: 0.0.0 '@rush-temp/model-guest@file:projects/model-guest.tgz': - resolution: {integrity: sha512-PxaK9/n35Oo9K6MwmJnyf0gL3Q6NRUpD5EgZkFrxKAzcglNOWjJLuhRWQ3RRwOUZEs0jAr2s7rK/lqJq81DzQQ==, tarball: file:projects/model-guest.tgz} + resolution: {integrity: sha512-QFp45PWxQSbItstTwShE5rmYl8vEiqXzzRkqKVrR+BJwxLCEj4ScrQTYNDZz/Iy6EucDCVv5/YhBPbwL4NTq0w==, tarball: file:projects/model-guest.tgz} version: 0.0.0 '@rush-temp/model-hr@file:projects/model-hr.tgz': - resolution: {integrity: sha512-t21l818wr5YqWrjkvMSfyY5wX3eZvbG0WKZe/OwQ3Zj8mTAtAY7vAbCTo4TNdOnwRsi5FHVOKOpl53j8lA97Cw==, tarball: file:projects/model-hr.tgz} + resolution: {integrity: sha512-p7sjzBWuYEhOe9tmjvfnRY4KmqIMKrrQL7xCgh3YpL3Rd37Ho2H5Hdr+arVaijex6o/W0ii0iMMKGvT221wIGg==, tarball: file:projects/model-hr.tgz} version: 0.0.0 '@rush-temp/model-huly-mail@file:projects/model-huly-mail.tgz': - resolution: {integrity: sha512-8bsDEp5Rzp7CgRDgUFYmxcE2aPdAlj7+Bn9k7svAB5F6Oxo25BKKImBdFWWPLiTkp/IdRRXlfHepdY+WArtyGA==, tarball: file:projects/model-huly-mail.tgz} + resolution: {integrity: sha512-RI46Zx9vVmoBy28nvghN4epIBhHoxuwIiBGBwBTwzR6KIvuQVFC/uz46FvDbHRdNZqNXiGUTlLms4BwIWoh4qA==, tarball: file:projects/model-huly-mail.tgz} version: 0.0.0 '@rush-temp/model-inbox@file:projects/model-inbox.tgz': - resolution: {integrity: sha512-JSpfnuU3ClFCsQ72MwM14tnUnjHkkw2tYpsXDqK9ctZh4Q2pYBrxn1u13evLbpHVglDLX7qrzXzIoDDOeiMeMQ==, tarball: file:projects/model-inbox.tgz} + resolution: {integrity: sha512-sPW7HCvYF6kNNnzic2VEfZ1ZWc/LniRdBKDSfCG0oLCBXzLyZ/vadxY+V1W60h4Yhk6rjQS9ocavxSLVaPr9Hw==, tarball: file:projects/model-inbox.tgz} version: 0.0.0 '@rush-temp/model-inventory@file:projects/model-inventory.tgz': - resolution: {integrity: sha512-bPHTZn0De2MAHxeDtXquaHlUD05bRAMlqi5ThY4id8mgNnItMQF+Lc/OrJCj0rCY2hl0va28n92CfZhMUn4utw==, tarball: file:projects/model-inventory.tgz} + resolution: {integrity: sha512-kJkGs30iSyS2ipElw1fOSO83ZvePaS2cwEUNNhu4IO1BYEnZgh9NCL/sjJPdxGupMU+uqID7A93eduwuMnUlxA==, tarball: file:projects/model-inventory.tgz} version: 0.0.0 '@rush-temp/model-lead@file:projects/model-lead.tgz': - resolution: {integrity: sha512-Ah9Jtb+2q9bxKKDS8udp+4vABLtQqunb+jISSPbnm3m/Toof8m7B823QjCJnEwluf7oziH3reMT7CQ+tPvyDgQ==, tarball: file:projects/model-lead.tgz} + resolution: {integrity: sha512-FLggYQgbW5EuvHuZce1aXGBuoZ7oyAUs0NQsJOGUxbS6imCHUCPlBNs4VeTaiU+aG8g96zBh0VU4N8Y3tTAWcA==, tarball: file:projects/model-lead.tgz} version: 0.0.0 '@rush-temp/model-love@file:projects/model-love.tgz': - resolution: {integrity: sha512-u9XWyHGyJ4x98QntQc+vYLNMq9wzLRAOaM11x0EG4/zCi2txxGsvKq8BHG092H9rjBKRqD+4AurInJOwhCIgfQ==, tarball: file:projects/model-love.tgz} + resolution: {integrity: sha512-+L76ESnyEoCISAbMaJOGPaKNUb7lmFR4AzP1Tnw2cIezWR1ufn/YUbICVnTGByvYPLmB0DElJnKTSixpNYOYhg==, tarball: file:projects/model-love.tgz} version: 0.0.0 '@rush-temp/model-mail@file:projects/model-mail.tgz': - resolution: {integrity: sha512-5+Qk+zC1wAHuOCk/gvFTPRu2JJAW9yp0+unXnxwZgglBs3BErwPmkcOKbJKlkiyoFnSP85Pv5KBkJYCGpAQrQQ==, tarball: file:projects/model-mail.tgz} + resolution: {integrity: sha512-zzQnttCqIuTmcUNwDo7zQ30D+eXsuO1oaqG4uBCR4WTDxf+0VyNbp+93R01Vq9EsuOumLcC0PPB5lcZMqzj8ew==, tarball: file:projects/model-mail.tgz} version: 0.0.0 '@rush-temp/model-media@file:projects/model-media.tgz': - resolution: {integrity: sha512-lmebZPPIAjTOEOFScJjqIzLD5TzNX6l2E3GcJXdXHByl+bIHZFPg4SbFj5LMd8zOk+vPpldHxQLxfN4mSZGBVQ==, tarball: file:projects/model-media.tgz} + resolution: {integrity: sha512-jvzySX39pzQ1hmnRCfoHet/WKVXxBPjGYaj+uuxGSYoMAcEIC7s85FsekdPaOFNEcpt01uEasqCdc3hyZQcaJg==, tarball: file:projects/model-media.tgz} version: 0.0.0 '@rush-temp/model-notification@file:projects/model-notification.tgz': - resolution: {integrity: sha512-e0D9BGHVJh4vY6oqByEKAIyy2gvJpDvmUuh4Tebj4NNC/QeEDxGL/IGqmAey3I2j6adGjzsrgPo5doElL9gpeQ==, tarball: file:projects/model-notification.tgz} + resolution: {integrity: sha512-ot2reVL5v63FeKsFsyHg8AnQHwS3OQJHVEsdWFyFH7KKvI+cHnJ+w6UiNIJcLg6E9fEurNthwEKptL2tZKdBHg==, tarball: file:projects/model-notification.tgz} version: 0.0.0 '@rush-temp/model-preference@file:projects/model-preference.tgz': - resolution: {integrity: sha512-NS9Z3lbUSP3bSVDXEJlg/5EbDbWJtei7RGlyCuBxXQNNnw/f1i+IYStiAqKw4/xRxBognYEHmFtToB/R3bMb4Q==, tarball: file:projects/model-preference.tgz} + resolution: {integrity: sha512-6KqpU427vJVqHbMAfFzFyYg03h6geK5GKMNALmtvr/wH8nvCsagGrASSn9IBZdtytpTxDcWV1ojRcXgmrHxIkg==, tarball: file:projects/model-preference.tgz} version: 0.0.0 '@rush-temp/model-presence@file:projects/model-presence.tgz': - resolution: {integrity: sha512-79VhCLCSZ7PUCUqdqTLHblzmnMmmMgOVMrtLgq1o/b8nE/Klkndm69K300DteUhqjcXB5feki7u7ev0YfRIPTQ==, tarball: file:projects/model-presence.tgz} + resolution: {integrity: sha512-Jl2+KVaSgSzNYC78qwj+Wz1+cFaVEBITQHGpngok3MY+b7NRwg4bqVwtGymPKc/ZcEhfXzUZWj01YAjWoMwWBQ==, tarball: file:projects/model-presence.tgz} version: 0.0.0 '@rush-temp/model-presentation@file:projects/model-presentation.tgz': - resolution: {integrity: sha512-XQPHfpxTHfbiqas5Z99ljrTyBWPGVzg18vMkXuiSgJDNdo9lnMIE2cRrkxe0h2KeXk7i494YaTWCFD1pTI8Rcg==, tarball: file:projects/model-presentation.tgz} + resolution: {integrity: sha512-TTkN+dNgVdRXf7VEhgKgJQe6Lg2CyDDhq5Lg2rAOdzsBEGLLoK+jgdpgVyxyN/0aA6GZhB1JiHtqNcpyHrPeaQ==, tarball: file:projects/model-presentation.tgz} version: 0.0.0 '@rush-temp/model-print@file:projects/model-print.tgz': - resolution: {integrity: sha512-S+p/e0ZpBv/orLsO1l30iAqRFi5tTd+p+xlQYAaAAA2cL5qROMJdLHrqJBuuHVp13S54weGvZ9vdA3Dsb9iJFg==, tarball: file:projects/model-print.tgz} + resolution: {integrity: sha512-VjRfH3WC/d0qBHpX0/38GMLZhEfmWj/NbrzhrL+R/QmVORNv8Lz2hl/0t5BnOR+k52TtwHkeN1scdxeI5rVHOA==, tarball: file:projects/model-print.tgz} version: 0.0.0 '@rush-temp/model-process@file:projects/model-process.tgz': - resolution: {integrity: sha512-GHolh0EAGM37BvfS850N1F0EEFz36xJQjjYZk5w2RaCUetKqToEy9wSvDY2aeFyhuqdGb60vGIlZL8frXwy+mg==, tarball: file:projects/model-process.tgz} + resolution: {integrity: sha512-6omsAIIvJhfTEhQvmDUAiuaAjyk8dJVlJAKnpYI+3/KwwFIeeRcFbWFCnOvxMZB8vPKHHJ6sOOxtQfoKBxznBw==, tarball: file:projects/model-process.tgz} version: 0.0.0 '@rush-temp/model-products@file:projects/model-products.tgz': - resolution: {integrity: sha512-5Mn1sQpQsfEKtEG7LAk3snzDLZ3oiMKHvbU4tIjajtNBo9tXmHN0a+JdyDpuRr317st6T94OHTou/UllYDVivw==, tarball: file:projects/model-products.tgz} + resolution: {integrity: sha512-jX2qii8QU5+YZiO6k0507itv5SSaQ6OEz4cEDdNaf19I2aGlpuHFoD1pOrQGvoYLYYOkP/A4m8Q6+voL3BcA6w==, tarball: file:projects/model-products.tgz} version: 0.0.0 '@rush-temp/model-questions@file:projects/model-questions.tgz': - resolution: {integrity: sha512-xFHbHcbKcpFddrqSl6Ih+wz0NqnPVC4Pf08tw2Kkt5UpRRIFV36sk6mAjECxERXWTeKJZ/fQ+TMKWoF4dBfo5w==, tarball: file:projects/model-questions.tgz} + resolution: {integrity: sha512-NRYmA+BJTbWAtPijtVckm6XWNygeebr8rj7SdV4M+nX/QLwRDJRHZ4zRgX3xty0/uDt3DHd8VQ4DfplJ7zIgxg==, tarball: file:projects/model-questions.tgz} version: 0.0.0 '@rush-temp/model-rating@file:projects/model-rating.tgz': - resolution: {integrity: sha512-DGSxgTEbAxs09PwCmvWhwrBRm0Q2fcYX4LB5A5kX4ZT9bGuqhdPep/vEf7d0fLpTSjkgi5GTmpLqJx7bG1vyHQ==, tarball: file:projects/model-rating.tgz} + resolution: {integrity: sha512-4nfnFaBDRRfUP1hpeK1dYMm3JxUDcDjpQHkd2Y+jECDSaf68kcLLPeBTN9vUM4nt8YTEaG5SWD0F5CiTLUFZiw==, tarball: file:projects/model-rating.tgz} version: 0.0.0 '@rush-temp/model-recorder@file:projects/model-recorder.tgz': - resolution: {integrity: sha512-H3BCEavTjNW9dqMWwWEGZaZowdxdSTyo2Hvt8T2pAf3JAzr+5bjw80Mm0NfdrVwPffrvkWmLrHODwZlMlF5ntA==, tarball: file:projects/model-recorder.tgz} + resolution: {integrity: sha512-bfQp/2xY4KzG1xORwKbW9atkaxLgoaXBHLTHDgSweqYNl0rIQJ9GWYFsFjT3ozierpjj9t4+tIz0GbBMjJMTMw==, tarball: file:projects/model-recorder.tgz} version: 0.0.0 '@rush-temp/model-recruit@file:projects/model-recruit.tgz': - resolution: {integrity: sha512-VzBYx6Fo6Yjj36U9uC0u6x2j36QQOQUQ8KkVDEJYqjdLV0YTXEfk9sYF3pZ5HMqyD2oB7Yyk6unnMLt+efAe3w==, tarball: file:projects/model-recruit.tgz} + resolution: {integrity: sha512-QMigI5iyuZ2PHQDxbb2rtz2hPh/WDHCkaU3dBOVwgYehJHdwJszx20p3dI4fEI1z1x1+vwCaCwBDV/ZWQ2B01Q==, tarball: file:projects/model-recruit.tgz} version: 0.0.0 '@rush-temp/model-request@file:projects/model-request.tgz': - resolution: {integrity: sha512-VbIOpBaPl7qbkkVREh7oeqbG+q+YzWxhXj5xIfcffKXs5ahA9z3ZJcwfeNdGYUjZxoHf6qUlE45Uq+LXJdudQA==, tarball: file:projects/model-request.tgz} + resolution: {integrity: sha512-vD8yzP46F5+f6pOTdTcy4E8o4mX0OyO7iuPqw+YQEmI+ERm/iCsa7aA+X3qckTEzDj5EpPVkdD7VkBL/ba2ySg==, tarball: file:projects/model-request.tgz} version: 0.0.0 '@rush-temp/model-server-activity@file:projects/model-server-activity.tgz': - resolution: {integrity: sha512-1QMqOKyWjV16tQ0Jeum6uk7JUgHBgNSef7Pm/s+mHR5G/NNy+wR4yA6rVo+5TOhc5Cgl8pF4sO8ke2PM2MPQ4Q==, tarball: file:projects/model-server-activity.tgz} + resolution: {integrity: sha512-XtzKU8tntdVaPCKXVZiG2T4Ux6kTuPBQkO0Tn/u5JeA6KtZm+gN4s4RaHUEZqAKlHgeRXzV/+EogVq3Kbu43AQ==, tarball: file:projects/model-server-activity.tgz} version: 0.0.0 '@rush-temp/model-server-ai-bot@file:projects/model-server-ai-bot.tgz': - resolution: {integrity: sha512-5ZE2+vNryK/TMBjncgHnIt5WG/2C2k2wdXO1rM92Izwzsw4qYtxmPvenbw9yGrjyAZxaUeyXfbh28q5p1qB00g==, tarball: file:projects/model-server-ai-bot.tgz} + resolution: {integrity: sha512-NHc98lmYom7OeNhTSrA0Jgn6RxRiu5tSnkd4BUfDTnCs1pLobbHfzXoS9dCbdOBObahzdJElSTOFTWxWFYyCPw==, tarball: file:projects/model-server-ai-bot.tgz} version: 0.0.0 '@rush-temp/model-server-attachment@file:projects/model-server-attachment.tgz': - resolution: {integrity: sha512-HAoZoeJDE7IBePDDlMYHDV882ZGSlzMFnnDJvzRVDyf1hfDVECG0VzTNoNkCO8mHr6WoH1zSs4cGEddBaC9u7g==, tarball: file:projects/model-server-attachment.tgz} + resolution: {integrity: sha512-Rmfw8aDcehcnUF6DAMGyY+nukFvGjoaLRDAkiXB/2zL0HZPLpQh9HuRNrrF/KPp0TWDC0Iy/nggun80oBuVajg==, tarball: file:projects/model-server-attachment.tgz} version: 0.0.0 '@rush-temp/model-server-calendar@file:projects/model-server-calendar.tgz': - resolution: {integrity: sha512-NOVEmop+yTkOdx8M/5+iM1+c8ZUF5s8Z8UT4uIPboyuc7qZX9GLP+aUS5IAb0hjf5X85Nz36DYCNEel99WmemQ==, tarball: file:projects/model-server-calendar.tgz} + resolution: {integrity: sha512-xarUGxbgReMHlF/evmu7Al+lK2wl3L/BNLynFrGGoIS0/49U4ilRib9MvEYSAisJO8lsP3SWB+ulLZY9/6akSA==, tarball: file:projects/model-server-calendar.tgz} version: 0.0.0 '@rush-temp/model-server-card@file:projects/model-server-card.tgz': - resolution: {integrity: sha512-Y/VpHSS4/N9ASjCtakQcJV3Bf4ysijoyC4SKN1Dfn7C1mTZEaN6n5Z/KniCFVjY018/OV/wgq3+Szs+CbqBpvQ==, tarball: file:projects/model-server-card.tgz} + resolution: {integrity: sha512-5Vq7m5a7cNJ4chuQL7d4poObJ+DoKkR2QMz5CxGJ11KkNbXct0qMt0tWVFJ7NCrWGSSOV1UsmMekX9K/b1nrog==, tarball: file:projects/model-server-card.tgz} version: 0.0.0 '@rush-temp/model-server-chunter@file:projects/model-server-chunter.tgz': - resolution: {integrity: sha512-7q2Y6CZoR+qMn7FKyop0ntP3RKj85vaEghiCER/66pRIKKoTndyv4RjqeB9XeWRGM7boo0gGUJ3zCmFidP1dhw==, tarball: file:projects/model-server-chunter.tgz} + resolution: {integrity: sha512-j+ts2BTLjX3FRm1pr37Zr7dKmrjXCiIJ3TukgiYsbGEBIpwIl24SQijbXO3rbngdbCGnynnFqjX6oX6t2AM6aA==, tarball: file:projects/model-server-chunter.tgz} version: 0.0.0 '@rush-temp/model-server-collaboration@file:projects/model-server-collaboration.tgz': - resolution: {integrity: sha512-tMU5bEiF0xTbMVsi4TMW/QyPrOG2i5z1uyFNjgetIFOJlsotzXpAPVrVWbJaGAnlDMBLQvTG1hh0/wYFdlpKIg==, tarball: file:projects/model-server-collaboration.tgz} + resolution: {integrity: sha512-upNIFRqaEdwvFOZUQQdP0wefJGfcu6ywUy3tkyEppXutj9P5k/58oOvERLTK+l9atB8ytdIrvikZ4i0bik+f4A==, tarball: file:projects/model-server-collaboration.tgz} version: 0.0.0 '@rush-temp/model-server-contact@file:projects/model-server-contact.tgz': - resolution: {integrity: sha512-GPkTR8qnp4zZOETx/4AvuviMxzrrPXBXB8UHh4zBPfDGtl2YOixI1jyJ4c6RN+lhIFt9/WB4SedNC1/xD7jTzg==, tarball: file:projects/model-server-contact.tgz} + resolution: {integrity: sha512-Px4u56cRjeHk2sEtnkeA4EjCe54brwaSBjJZR78Utj3BGsgMigB0RjYsJNfmwonteRgN7qoOMS4HQgK5jy1Jxw==, tarball: file:projects/model-server-contact.tgz} version: 0.0.0 '@rush-temp/model-server-controlled-documents@file:projects/model-server-controlled-documents.tgz': - resolution: {integrity: sha512-zyFialzN3pgqKq7DlXATPHlGljKq6rhM7RyYmuC5OzVmX6gs5EQefuInqYP/0b1D1hDz7rEYq62X0fjmGtb81A==, tarball: file:projects/model-server-controlled-documents.tgz} + resolution: {integrity: sha512-ZvXqc+XL/gdSlrz9tAYrq2WqHWKhYxHAEghNgyfovjzIgyVET1MQBeLshSJ+QkM3dtLFR+0g1559d52zsp4BUA==, tarball: file:projects/model-server-controlled-documents.tgz} version: 0.0.0 '@rush-temp/model-server-core@file:projects/model-server-core.tgz': - resolution: {integrity: sha512-KGAnxkNSZs1wuh9CPvBeWL6SiqYPN+UTX6dQwJjAjVtai4XbooGzgA4um1HILPoRu0Mp3iiPPR/6IDwb21Jy7g==, tarball: file:projects/model-server-core.tgz} + resolution: {integrity: sha512-diSMya0cwhNEGRq5yTbFbQWNb8afbXbGKmmkmPuiGWXd4fHZ+ENPqf67dZXyn7ptbvVK8i12VCHJWf0JPQjcrA==, tarball: file:projects/model-server-core.tgz} version: 0.0.0 '@rush-temp/model-server-document@file:projects/model-server-document.tgz': - resolution: {integrity: sha512-38Bx+p7UgSmqwsoFGFK87geW3AQU3fH+uObMPLuNxdJOSXmWeastYjAsQJCUeCSHL3YRFXdnx2zZgXFN8n05sg==, tarball: file:projects/model-server-document.tgz} + resolution: {integrity: sha512-RtYy709eBt4nWgqGzRUgNkOKJVuQ6m0SrtZfWAh66u7mchiCX+hmiftcCpiwrR7/1y7MOIRkMs8EcyxX/lpOtw==, tarball: file:projects/model-server-document.tgz} version: 0.0.0 '@rush-temp/model-server-drive@file:projects/model-server-drive.tgz': - resolution: {integrity: sha512-hmki0wicUXo01G3wZwiUvLSEFxDk3SmZcKxKUmOCKsVbbhbcqjd+7Igkgb6lxihemAV488cEph6fREFuiGRn3g==, tarball: file:projects/model-server-drive.tgz} + resolution: {integrity: sha512-oIBeggfnssRoK9TXqWndRDyKikGjdumnK7S7V4gx/Kc47pTl2U4ll/LWR9kie8WePPrHPfxXmZj14x6g3Y3PSA==, tarball: file:projects/model-server-drive.tgz} version: 0.0.0 '@rush-temp/model-server-gmail@file:projects/model-server-gmail.tgz': - resolution: {integrity: sha512-UofqdMNWyIXWTF3jQfUFuRC/sfSo2Ftqa2+5Yh/EVlniEZs9yqqDm31ecYN0xU3cXA/sCqBnsS83YOb7Oq/BlQ==, tarball: file:projects/model-server-gmail.tgz} + resolution: {integrity: sha512-a/GAMt3Mti/5EZw+ljwW26tyMJ/YCk4EXVFe2bbxKmVXEaC1JYzcEBdyR8RcJll7ZAZYUddhrKwCzonbnpcYEw==, tarball: file:projects/model-server-gmail.tgz} version: 0.0.0 '@rush-temp/model-server-guest@file:projects/model-server-guest.tgz': - resolution: {integrity: sha512-/pmvy9IsL5R3fq6OcqIKArKoJV1RtLVT0SGZgidsJ2IG3THfzKloBu6zSEf/vkS8CLMvS8UhANAssn+vCrZGuw==, tarball: file:projects/model-server-guest.tgz} + resolution: {integrity: sha512-VfxH704hTxdJfNxlu5LDJ0PjHVU2grvSUW7YHtacFxdIOTrMQ+9l7EWA6/hIA0YBn9RpMU7UzfSkGyOdVKTIMA==, tarball: file:projects/model-server-guest.tgz} version: 0.0.0 '@rush-temp/model-server-hr@file:projects/model-server-hr.tgz': - resolution: {integrity: sha512-XMMXZ+AbMAa9BHx2xt/tho+znDH+YBwZ09dlPNF3sFE3k4eBzop44FAb37yoZ8IsSIxhF001Nf/bxtt9v/hW0Q==, tarball: file:projects/model-server-hr.tgz} + resolution: {integrity: sha512-Xxmbz5rARSUeH+dPXeBLWvh7EzdxX7Qdps/QQCixs5OmlDQeK+DoB7z1ei+rDrWUSiAwo2XCEirhhCpEj3/cCw==, tarball: file:projects/model-server-hr.tgz} version: 0.0.0 '@rush-temp/model-server-inventory@file:projects/model-server-inventory.tgz': - resolution: {integrity: sha512-+widHyY9AFqoWBiIJbU009+3nBKgwPinkwMkn3xnGlT9tDjNr7Bd+jmAZlBHpau2N49zFBpLsiGHrWKwEbJCPg==, tarball: file:projects/model-server-inventory.tgz} + resolution: {integrity: sha512-PlsiQFiw6o0aCppT+XRp5adkQTMvjJGtyjoTqT/LBY1sS8yZnwZYyeM5Rvkolj8FFf8w9Shk/X8dRjOkA6jjWw==, tarball: file:projects/model-server-inventory.tgz} version: 0.0.0 '@rush-temp/model-server-lead@file:projects/model-server-lead.tgz': - resolution: {integrity: sha512-0mlmIkzgc55C/4s63YAqTGvDPp9s19omlbJZ5kl6EDto6ud1CJ7RVKW6TRs8Rv21FgF3+6UhvcshCHGBSlQptg==, tarball: file:projects/model-server-lead.tgz} + resolution: {integrity: sha512-cA91BpZJmvbhB1vX/H+i0L7uvTPtbA9oIYwADiueglAC8edavFsGGWvQ0rc0BiILb6Q0hnD/f+tuRYvltAOl3Q==, tarball: file:projects/model-server-lead.tgz} version: 0.0.0 '@rush-temp/model-server-love@file:projects/model-server-love.tgz': - resolution: {integrity: sha512-/baH3q5Jr4GT23eGiFdBOufJETQcHiZL9Iy9ikw/Y4eoz2S0o6b+tOyha059OSASETzW0v9B63bgdLdL+cK/ew==, tarball: file:projects/model-server-love.tgz} + resolution: {integrity: sha512-oYf1VFU9uZ6VUgyaffuf+Ia26stSp0vXq7kf2oC2apWRvW83BAGnD1AaZXo/74QBJ/BKzQBUx1A1fVuJyg7Nhw==, tarball: file:projects/model-server-love.tgz} version: 0.0.0 '@rush-temp/model-server-notification@file:projects/model-server-notification.tgz': - resolution: {integrity: sha512-dyJeI7gc5IuMR530a6n7LH9/ahNVy5+6051V8zzx6rz6TfeStPBnXpfN6K3czxEh7DA+W1EDK19r0PYAU8TJxw==, tarball: file:projects/model-server-notification.tgz} + resolution: {integrity: sha512-PHFG8As4+4bgl59yES1bL8bdDSdSo1PZ4JB2TglTmPwixpFXGmBxkev6usZSeOIvOkUngQZUnRL03w57TteVsw==, tarball: file:projects/model-server-notification.tgz} version: 0.0.0 '@rush-temp/model-server-process@file:projects/model-server-process.tgz': - resolution: {integrity: sha512-85RsWLeWe1jZ60WUjVhLIbR+UZ5LSPNodel6zNC/W7MIK5bIU8YrhRYWEAZfgkyKzeYdeFM012sVxPa0oO44ww==, tarball: file:projects/model-server-process.tgz} + resolution: {integrity: sha512-bZk3mCKM9Vk7oJWUqTMLigjLWaLu1CX3y8bEz13HdTiBIRilb1sbQkoiZgUFvcm4ljvVktioQVTq6w9Ft8OVaA==, tarball: file:projects/model-server-process.tgz} version: 0.0.0 '@rush-temp/model-server-products@file:projects/model-server-products.tgz': - resolution: {integrity: sha512-4rrBiW8iqlfGhRpxXuOUnBK+APnBmabSNYrON48b44TdukFJCw1b9rEqCGRAAytDk5erLh43UWxDNkIjxjFDRw==, tarball: file:projects/model-server-products.tgz} + resolution: {integrity: sha512-ns9ztS7hxFmC2bcvIt8OpchB6ZwahyO3VrGUYWF8sK1DgXSJ2tYQjeaLdyz80tC5BSBBLOoxZeMYB+at8V5Buw==, tarball: file:projects/model-server-products.tgz} version: 0.0.0 '@rush-temp/model-server-recruit@file:projects/model-server-recruit.tgz': - resolution: {integrity: sha512-xdMmuiPUEvaMyffr7Sh3kFx29n+/6xXRXzu1S1PNdi45Fiz5HzO5kfjFbEn9URRmKp8tVpNIXAkSOQU8QHkPSA==, tarball: file:projects/model-server-recruit.tgz} + resolution: {integrity: sha512-BYZUWlK27po73G/MoL2WiCBvV02C5ww/FxBdj0xO1t2O8CZrzrnqXU95U6iYUyMR4NszQw9/rytIhd+uyzeatw==, tarball: file:projects/model-server-recruit.tgz} version: 0.0.0 '@rush-temp/model-server-request@file:projects/model-server-request.tgz': - resolution: {integrity: sha512-NvEbZP4iJz/GDsugFOAGBA30NugR83xZ3YaPw0AdPsbzk/45rX1QZuE/wkALvTmBbUp8g80HoBYw/fZ/ZS4Pwg==, tarball: file:projects/model-server-request.tgz} + resolution: {integrity: sha512-ShuTnleLnYAK4VGFqmdNUxdLOA/SDz6UOciaRHnpfMaAtN8K81Zcq/fHcH+l+VtpxLdgJJf7XIGkV8McYrZ+yA==, tarball: file:projects/model-server-request.tgz} version: 0.0.0 '@rush-temp/model-server-setting@file:projects/model-server-setting.tgz': - resolution: {integrity: sha512-yIJEw5Sv6TgEcs1xZ9pQwaECDVk7kbTl7aThvO774asuFxn0tYYAr8GI24+V/JlxM22lRkrIQW4n3AOccK8vjQ==, tarball: file:projects/model-server-setting.tgz} + resolution: {integrity: sha512-rizOUAhFUyQOyKarbsmsaNIK9a2Gc3wtRF/8OlZVwO0eKUDac8oZAaqPjfP5+sgRr8aSbbfH5up58pX/P20LxQ==, tarball: file:projects/model-server-setting.tgz} version: 0.0.0 '@rush-temp/model-server-tags@file:projects/model-server-tags.tgz': - resolution: {integrity: sha512-NHLB+sQ2e/Bp0SE1mWQFaSwVc4D+F/ASzYiWMUqnQsmGVRLyiGvGnMGwNySmrAVaQGUVH2I6YdVvjEw0i/zWJg==, tarball: file:projects/model-server-tags.tgz} + resolution: {integrity: sha512-4ksLtOpsM47f8PBBlOtYWGnhAck/7mIYwHMPeRbBbFCnXKuKhO7NVtiVOnsldpC7aXc8nJt3IPOxtumm4X01pg==, tarball: file:projects/model-server-tags.tgz} version: 0.0.0 '@rush-temp/model-server-task@file:projects/model-server-task.tgz': - resolution: {integrity: sha512-qKsFg+e6ywy0+nzFXWix6eZWTGSSv8Ao7mSts210iG11vtp43u2pDuveFPiM+bDsHp+4vWSRZnBdmBZhtAJ4wg==, tarball: file:projects/model-server-task.tgz} + resolution: {integrity: sha512-l1M68Pc09rGeP3au04o35+p2DAsjEFWIEfweINtwpnv6uHuVYfoe7jXLv3J4xhkSbJG2U8oVZf6YzDb5Kkmdiw==, tarball: file:projects/model-server-task.tgz} version: 0.0.0 '@rush-temp/model-server-telegram@file:projects/model-server-telegram.tgz': - resolution: {integrity: sha512-lGgJpLPGuphCuBT/UcVsXp9sOkidJOFyWjnkRiWIPXF62nvrmiKhu/t3RO8iC0ZXMM00ieladoxm1f3Mvd4c+g==, tarball: file:projects/model-server-telegram.tgz} + resolution: {integrity: sha512-Z2MO4lGof53c+piQXIsZgkgtJGnmRAuN/L773Zee84W+vAqT+tbrOkO3jpMKiC+3EolflyP4bC4dZJGc+n2VBw==, tarball: file:projects/model-server-telegram.tgz} version: 0.0.0 '@rush-temp/model-server-templates@file:projects/model-server-templates.tgz': - resolution: {integrity: sha512-NFlvAl9RkjTiqVvqH8tq872e/ksxW+LXjRnJc+xgG53A1/1m6es/raOEVGvzt44dVxadFnxggbx/fL4QEL9WUA==, tarball: file:projects/model-server-templates.tgz} + resolution: {integrity: sha512-SwnfZuJd4NXP0XTRmLNDejIIe/ygf362+F4RtooBAyqvIj1ozPcrc3XFu0imVpRV7UJTuW3jibwsg54At0YuQA==, tarball: file:projects/model-server-templates.tgz} version: 0.0.0 '@rush-temp/model-server-time@file:projects/model-server-time.tgz': - resolution: {integrity: sha512-RkW9gzAUMtppBbLb0McHuHrn8p0/X+ZkVU68nghhk2g3d+J0BwxwTjMgMFSA0x7Ixg2qixha+VpYMWOSK3aJog==, tarball: file:projects/model-server-time.tgz} + resolution: {integrity: sha512-g6LyYFeQshcn+7ke/rNsqnv91c2VGgXo27ucxxzDCe6f5TaXUGXemooc9ZgS/rV9vhMGXNmUmx+9S77caKmKzw==, tarball: file:projects/model-server-time.tgz} version: 0.0.0 '@rush-temp/model-server-tracker@file:projects/model-server-tracker.tgz': - resolution: {integrity: sha512-yTo00fFm4DXvyj5wU7qLdPRXlT3zXgxspDejQOKfthUYo9XcCpHIeOFsmIQS/HZdHD/BS3OTPKFwKcusFmm7Aw==, tarball: file:projects/model-server-tracker.tgz} + resolution: {integrity: sha512-0L0NF2H01rMLFrWZ3ZNe00RrmDQ4ijLhTQHfYxacyEV+oFG4SJVYb0QTC4OJUnZhC9/F4XmAqdq8yQB91Oi1Tw==, tarball: file:projects/model-server-tracker.tgz} version: 0.0.0 '@rush-temp/model-server-training@file:projects/model-server-training.tgz': - resolution: {integrity: sha512-Bfbnb3g8BwgJwJtTy3QRx2JdrgLanZM0nELkv/LGDrGJQ6EpPWWy/HN7QYD9ygR++b5j7jLcAqGVwS6NNGQg3w==, tarball: file:projects/model-server-training.tgz} + resolution: {integrity: sha512-cYmOgqtJ0WjVSg5LQZ0BdSCwvtoCDSjDlBE/5ChxKqH3ClAONmbvlwsN+FSAfVsFoJrdQHE61XXa5JWa1YXUpQ==, tarball: file:projects/model-server-training.tgz} version: 0.0.0 '@rush-temp/model-server-view@file:projects/model-server-view.tgz': - resolution: {integrity: sha512-kC28jESIHVyiqsx1tAP1Fxmr59v+u0EH5n8JYiVFC0MltdzYcOiCaeZU+2Zqoq9K/D+iUrHaQTPNRPfKPmFNXA==, tarball: file:projects/model-server-view.tgz} + resolution: {integrity: sha512-pjPfdfIa692z9Uh+fvuYcEfp3m2q/s5WDL3SAvq6tpJYEMfC1U8DFwSSRH6baNKkOJ0/Np2hAc2vNbgLtsUJeA==, tarball: file:projects/model-server-view.tgz} version: 0.0.0 '@rush-temp/model-setting@file:projects/model-setting.tgz': - resolution: {integrity: sha512-OXWjb5RuC56oSQxWBsymEOlErd+qGu+10fwDQ+JGUmkPknbVoIidyEgkRGWqzcm1Owt7fR/fCuBIkjuXqpbjDA==, tarball: file:projects/model-setting.tgz} + resolution: {integrity: sha512-NEVCOfXZxB5CF2aARRCd+FPJQsggOfy1OfYugOvgBcqdxekdBUD+5BoXXnpD4EpGuEO1zSbDuNHc/JinKGZ4WA==, tarball: file:projects/model-setting.tgz} version: 0.0.0 '@rush-temp/model-support@file:projects/model-support.tgz': - resolution: {integrity: sha512-BlNv81hujZAv5d5dS+YDOD/uhiAiHbgKoUeMMTrUEW1Fpt3bGu5HfCY+nWQRSca1Muyt4yAHMO30OTfySPJPDQ==, tarball: file:projects/model-support.tgz} + resolution: {integrity: sha512-iFiXCzrcCAkiW38HQQbvb54D88yLJY0XCjNtpBYxRtiZhRqicb6gxBuvADSZO8pwQ5+Wbc0aZfz3oqBD9xaZ0A==, tarball: file:projects/model-support.tgz} version: 0.0.0 '@rush-temp/model-survey@file:projects/model-survey.tgz': - resolution: {integrity: sha512-VjShZ8bfRGAFsR7bEAUtUUghVffLXVBzxrrYxHcwayNv1rC4ZYCxv8iMEg4Y52qlMUk52nVWryi2XDyE4+pcwQ==, tarball: file:projects/model-survey.tgz} + resolution: {integrity: sha512-ckbX+ggZdLEneCn2PjFwlDoT6q4QmdByVMM5bfi9G5pSa3JMq8l3qqmikuShTmH2c475tNvXjJKg2FxtnmG9MQ==, tarball: file:projects/model-survey.tgz} version: 0.0.0 '@rush-temp/model-tags@file:projects/model-tags.tgz': - resolution: {integrity: sha512-lMPxzOEyroYbkKqZ3vwiS2yWZz0Xmc/ilOROL/3Poki4ttIHaL2sz/AHUP8I+f+5AQHqD8aMTyWMDczoK3asCg==, tarball: file:projects/model-tags.tgz} + resolution: {integrity: sha512-s5RRfGVzBk3f7FIu6XH6Nai+vx4hqlEIW6rI1zYwqqq7NAkv0yMDg1FqwJgl8bHQxNQh0CBsm28bhAnt4DWQqw==, tarball: file:projects/model-tags.tgz} version: 0.0.0 '@rush-temp/model-task@file:projects/model-task.tgz': - resolution: {integrity: sha512-TOTiMlkgY97kYASz/3Uy197q5t4c2/i91+yEXFHuev5pNwGQNp7kOWFpJGvNW7CVlT1nonfCN2i4WO7I0WmwFw==, tarball: file:projects/model-task.tgz} + resolution: {integrity: sha512-1PEjT1A4x7V0rlWqJ7wsmTy06gZz0mAIkP4flUh79bdF/Kyw/7J4PMiBvgM54aEYUA1zbSV97jvJ8pNP4BvEZw==, tarball: file:projects/model-task.tgz} version: 0.0.0 '@rush-temp/model-telegram@file:projects/model-telegram.tgz': - resolution: {integrity: sha512-cW4dCe7u7+rz1+tId61C01xxa+aQtfs92hWFR2tUm1NdGXBwn0PxrdXYDgxiEHfPW3qX7BxsBajpnxYSqdaTjw==, tarball: file:projects/model-telegram.tgz} + resolution: {integrity: sha512-+4802O25v0KFug7gKXyRpbPI4LuuHJi2TyjbTrY3NHSVgDsdEEO1yDHrplUn1kZfr21Hid39u+4Ri5hNDYMM5Q==, tarball: file:projects/model-telegram.tgz} version: 0.0.0 '@rush-temp/model-templates@file:projects/model-templates.tgz': - resolution: {integrity: sha512-N2+sLTed/3rMZoyi59YMuj4swWV1UuqrGH2dABNryTdpEeJ4hE4RhpOn4Hb8qn2qwIYCiOTW3GyF0U05HQeolA==, tarball: file:projects/model-templates.tgz} + resolution: {integrity: sha512-0oVSvIWgUXFiBw94UP8XcnTmozeqnJ1IU7XaIOdtdoVqT4OPBT0DIvMOYC20jMICyvtk0EjVTuq1J07OGov5GA==, tarball: file:projects/model-templates.tgz} version: 0.0.0 '@rush-temp/model-test-management@file:projects/model-test-management.tgz': - resolution: {integrity: sha512-WJAIlHF5rMGkZPAlN9SWyu1TVwpt2Ns7sF7Rl3PvklIHQdyRJwjmxsjIzAR2KMWORT5mILvWb+NKlFVX8vZH1Q==, tarball: file:projects/model-test-management.tgz} + resolution: {integrity: sha512-WD5FGojw2JLvA2dvUM2rqT14LinfLgQyaf5ORVmXnO4zyPZVNGKZ6TmfKSotT1egCmA8F34KYnar4H1lEElBQQ==, tarball: file:projects/model-test-management.tgz} version: 0.0.0 '@rush-temp/model-text-editor@file:projects/model-text-editor.tgz': - resolution: {integrity: sha512-ragvOXj/BqmDm0by3XgSv2zb7oBpju/Nd4pS3yhYJT8M6aP8yftJa0w847v5KNlbkZKrXp3VKPhQzduZrlMkeg==, tarball: file:projects/model-text-editor.tgz} + resolution: {integrity: sha512-bpgtpJuC9paAgqTFsBMVi7xz+FtTUKj+/7mgXGon+YMPFe+u8aBlek+9JVUR0aS8+197LCuBNabd4fZnMBqZDg==, tarball: file:projects/model-text-editor.tgz} version: 0.0.0 '@rush-temp/model-time@file:projects/model-time.tgz': - resolution: {integrity: sha512-aH2SBUBIWepxgWgYy8FKSBHU4bvQe1Rxj+dSeqfVM5ljSI9QHDVbqcTBpOPKlZbaiTryAl52i5bPq2astzfnnw==, tarball: file:projects/model-time.tgz} + resolution: {integrity: sha512-4SPI/gK0YZUF7VGyP+lENCzsYY0SRMiktoyjuQehYkCju45wI43oev4G4uvuEJHa4hDMZaqgUYWb15y84OrC6g==, tarball: file:projects/model-time.tgz} version: 0.0.0 '@rush-temp/model-tracker@file:projects/model-tracker.tgz': - resolution: {integrity: sha512-YqtWQ5Cd/gEaO3qpXw+cDdNq+GLVDyCsplLBUpPvwy0ZuLe3uf4CW0D7YtAR1gr+J8Ul0yli7oxYmdssQpMBzA==, tarball: file:projects/model-tracker.tgz} + resolution: {integrity: sha512-toDBe5ZCV9ydtgHPQW+oWvjw4vHWGGDE+xCyKzuigqU1wyFYT2JBCdsAN7H21/awX4TL3/aDhE6GQe5A80qADw==, tarball: file:projects/model-tracker.tgz} version: 0.0.0 '@rush-temp/model-training@file:projects/model-training.tgz': - resolution: {integrity: sha512-cUHK+3RnWCXJtvjX5dkE/Vv6Y0+4WT6ri1VcAG22QihYnuud3qq0fBPdZ6T2Lb6MiW4Lg4w1A0J57LWFxgbqCQ==, tarball: file:projects/model-training.tgz} + resolution: {integrity: sha512-yUL3oYmiWsLogqWRO+X8LR5U3Q/YaEl3JrxAn8wxIr6cCqRMcQG7NBxBUDs4hxRMRdVBgl3Ol2/K/LLszRJVrQ==, tarball: file:projects/model-training.tgz} version: 0.0.0 '@rush-temp/model-uploader@file:projects/model-uploader.tgz': - resolution: {integrity: sha512-LVtTPB51vbMXUUZDkXOjQmtEY/rUsrSyiLC1IeKoWXVEwtNjLqJgr+S0ZcsCbPrmKtohI2MOmFOmA63HwYiKrw==, tarball: file:projects/model-uploader.tgz} + resolution: {integrity: sha512-y1ne57S5tWi8zrFsxkMdL2f3VKbEWZOlpRe5oy48Uxc7TKBqtdf/rMUYqEb3UD0bKlqzmgGYSvF1cmVvRcESUA==, tarball: file:projects/model-uploader.tgz} version: 0.0.0 '@rush-temp/model-view@file:projects/model-view.tgz': - resolution: {integrity: sha512-WwcdYpEnEiKujuywKlGMQ6F09sVb/QINXhM0/qehxwm544dgZHmM2ZPTPqh2IgC/xjUTew8iXIzXRLu5D21yew==, tarball: file:projects/model-view.tgz} + resolution: {integrity: sha512-IK+PY5j2D9GaLOEenGEYqeolIhObBLcR1FdMEhBWhMkBQsopTgiNFJaWXGFGNSWVWrFAY318vIKsKFjQa5TWow==, tarball: file:projects/model-view.tgz} version: 0.0.0 '@rush-temp/model-workbench@file:projects/model-workbench.tgz': - resolution: {integrity: sha512-cOf7TqHSzsOxROGyHNLJWg5X4mE8sP2ywSLXI4FUaJLGPZDDt9pEuFoP2MqRQwgLzFwW+zbu8PR9SqP+vj1JTg==, tarball: file:projects/model-workbench.tgz} + resolution: {integrity: sha512-Fk3/4coKQJBZGrWIDOitP4RiQlGdmCsi++I7GGIa9DHAcjZXUu4eFql6euykio+vewKPMO4TGp1zpA3QdMNKEQ==, tarball: file:projects/model-workbench.tgz} version: 0.0.0 '@rush-temp/notification-assets@file:projects/notification-assets.tgz': - resolution: {integrity: sha512-Ht2GaEdBn0xWTod3Dy94KpQ7tP1vyvAH3U1QZiGlTPx8cqx8j8k+csiZmAQFBlUxsjSL+qMYVJHDuab56vjp4A==, tarball: file:projects/notification-assets.tgz} + resolution: {integrity: sha512-ed+6rr8Mve6JQ0nWXNLeaynzB7BycsadKLewGC19QWtL9NWcVjKF5b+xdetPfg5C/RvmlxkOTBMXKrowEBm/xg==, tarball: file:projects/notification-assets.tgz} version: 0.0.0 '@rush-temp/notification-resources@file:projects/notification-resources.tgz': - resolution: {integrity: sha512-HtVYGrop06rUoqwKc0OGFeQgIRbVr9JrIHo68+GV/7Zz6hXmdk/Fv6G2DjQmUQzKPgYx4DuGwNftphJW0m0S+w==, tarball: file:projects/notification-resources.tgz} + resolution: {integrity: sha512-DSukWPq5pvzNW67qsrC93dTTWNBEdL1TKLYW31QS6yWlryfmPkcK+rSXIUGjFDdXy+CRwGsbR5aNwNI8oZSqXw==, tarball: file:projects/notification-resources.tgz} version: 0.0.0 '@rush-temp/notification@file:projects/notification.tgz': - resolution: {integrity: sha512-A6sz0FfJ60QkAfYMPZ14AV/SdLNV48d6aQ1EAjiw7D/6DIThszeCznJ6Tnm9P3KqGEhtihL0QN3vGk1EKhOgHg==, tarball: file:projects/notification.tgz} + resolution: {integrity: sha512-o4gI3mwzQaYXtdx/58XsovMwCEc3HGt8EHz4XYykn/eXb8raqd0Fplx2axyPqhzTDXWJYqUs2ysudBA5l1m25w==, tarball: file:projects/notification.tgz} version: 0.0.0 '@rush-temp/onboard-assets@file:projects/onboard-assets.tgz': - resolution: {integrity: sha512-X+lg1ZLS+DkcluMTBgn37kZCobEANR29SY6izdJSSIJN++GU3w4NAr2aVS15wVqw5ktwr9M03Q7QYD2/8rT/aw==, tarball: file:projects/onboard-assets.tgz} + resolution: {integrity: sha512-r8HK0yeEOCLd10SZBKXFQWOjJy7osFsyij1uVqGRe7YizL+d8kAz+PLwGH4GZodCnjy2X2pNODyRrl0SOfkjMg==, tarball: file:projects/onboard-assets.tgz} version: 0.0.0 '@rush-temp/onboard-resources@file:projects/onboard-resources.tgz': - resolution: {integrity: sha512-Me8SGnjRUk+PN73tmpF4HuI47kJ4y2+cCO0Z9Qm4drKJHkmXdaUFYKkO1zrVaZIhQsrEaVOIrGAEPETtTCILlA==, tarball: file:projects/onboard-resources.tgz} + resolution: {integrity: sha512-FlGPFNcxoXSA4Qw60GVeHTYfZEAe7U1hyuRZNOIGQy36yPOZ1l1gL9AykyDo2KIN6dLkHp+K+n3ineqVtEzowg==, tarball: file:projects/onboard-resources.tgz} version: 0.0.0 '@rush-temp/onboard@file:projects/onboard.tgz': - resolution: {integrity: sha512-+y279JdaJEWLX6sPrPvf0KzD0vb7coACaddSv9/Gb75qSf//YnFD23u/RqfvWDgX+GpxabbSAk3VfgH8rxnEGg==, tarball: file:projects/onboard.tgz} + resolution: {integrity: sha512-qYtU/LmSuG5XaNZnrnq/GpaunaP6UzowybH9Fq7MY+InfAk+uWzzEiBAg3EeqcW68YGt6IjO+0kYgOexdYSRRg==, tarball: file:projects/onboard.tgz} version: 0.0.0 '@rush-temp/openai@file:projects/openai.tgz': - resolution: {integrity: sha512-xEPwNN8hBKZUuSqJvnffz7R1Yl6Hi8rBBDv7481JqrDJQ5jIbemGzO/1oG8WrZ0gL0YTia8qhId9sDwApQJsNA==, tarball: file:projects/openai.tgz} + resolution: {integrity: sha512-MecdiCjmWA+U3uM94y29IZeSP+eXqjF9kX2R5QnmIxgMSO7KkfEXhsTahGbpvuyu6+Qb5I7yqoJ6rq3ZlqWruA==, tarball: file:projects/openai.tgz} version: 0.0.0 '@rush-temp/panel@file:projects/panel.tgz': - resolution: {integrity: sha512-HsfJ8xyxzBNY3mLc2Ft3CfZlOYjgTJyPwv3nl4KYYPgBkG+qFkaoIKZlFOgaHLV5PABSjHdmJw88pL7fLKu+qA==, tarball: file:projects/panel.tgz} + resolution: {integrity: sha512-UjYxVQp1tOVy+clTyk1YF5qxITZmHgVsGPBXzpyDshqMH797CpaUzeTJqpGm32QTmun9Ip2ts8hF5vewgMbRuw==, tarball: file:projects/panel.tgz} + version: 0.0.0 + + '@rush-temp/payment-client@file:projects/payment-client.tgz': + resolution: {integrity: sha512-L4QolbujKK9syqwnER1Pete7Jrg8m1wKQsjbwbMsPkjV3dvCXAljBIwcBCZJ8hp5u6/lDHQPRyqYU6V5QM5UlA==, tarball: file:projects/payment-client.tgz} version: 0.0.0 '@rush-temp/pod-account@file:projects/pod-account.tgz': - resolution: {integrity: sha512-7Qb1w9VCFpWVKlnl+aFO3H0e4OtzGp+wTmHWMbQbnufQqC0LJaCpgBpsCOfFtyaNCb3V5lHR8iDOsFVPOlsduw==, tarball: file:projects/pod-account.tgz} + resolution: {integrity: sha512-QO02uOmhZG1SRwBZCspQECnepEZsD36UCOTQqEKKeoS3R+Nawj1eVBCLKYtbaB+jpieTWzNiBKu+YH9wbNfW4w==, tarball: file:projects/pod-account.tgz} version: 0.0.0 '@rush-temp/pod-ai-bot@file:projects/pod-ai-bot.tgz': - resolution: {integrity: sha512-JVT3p+7iKYUaHxP54oBn+s1BSISHpTPEfpP9ZytjHv388C1f83hH9JH8dhuPS98WR6c07btXEfl7z5Q7EjFM9Q==, tarball: file:projects/pod-ai-bot.tgz} + resolution: {integrity: sha512-qWp1Xt4+123ev5g50U1+kb+jivg4xv2/3g1BpTjXhKOvCLVmqVwvCBf/ojEXWrdk38qP+TT7RCyZkXfFg2LaTw==, tarball: file:projects/pod-ai-bot.tgz} version: 0.0.0 '@rush-temp/pod-analytics-collector@file:projects/pod-analytics-collector.tgz': - resolution: {integrity: sha512-sTNn5aMtMzugrLPC5+oL2UeIHtYgxvasS3+zqkUks8fGTQXpdDZW1tCTh5wfMKom0U2SdGhbZcKLfbffGITuJQ==, tarball: file:projects/pod-analytics-collector.tgz} + resolution: {integrity: sha512-LhpQynPQpUHJzS8F1+88De25VS1XDM9rENMuE/odllbNqr1btFY2PfduQO3xa0SEMEA2s4vI3pQA8yy6GiMGwg==, tarball: file:projects/pod-analytics-collector.tgz} version: 0.0.0 '@rush-temp/pod-backup@file:projects/pod-backup.tgz': - resolution: {integrity: sha512-3QxHtYTwh/PaOFyBoR3nkz+kO0F4teF0Bdu/gW42ry3+FthLiVqZAadUwtN9nHDUhwksg7GhB1IFcJ+seEKvxA==, tarball: file:projects/pod-backup.tgz} + resolution: {integrity: sha512-dxcw4aEprMM+S04wUTS0eB62IOKJXrnDN44/Blw9K7r3VnAnwFHH/h3ntkN1Iu7uQnRYX6+4lnuRLHMC5cKCgg==, tarball: file:projects/pod-backup.tgz} version: 0.0.0 '@rush-temp/pod-billing@file:projects/pod-billing.tgz': - resolution: {integrity: sha512-+vNUoNj3TocnxiJW4Bh0pWvVH+bfzxjbtGYW75XH9K3zpW9u0PJ1BrydBlQoslY6KJpv7hAD+16zytmDCkrpTA==, tarball: file:projects/pod-billing.tgz} + resolution: {integrity: sha512-MHXr5iKo01bk26xQfmaXDcn4F7P1s42l1fKpHiA/abm0H86mcWtKNrgxLRWERL/1rDx764MpG2D7VTacke1kMw==, tarball: file:projects/pod-billing.tgz} version: 0.0.0 '@rush-temp/pod-calendar-mailer@file:projects/pod-calendar-mailer.tgz': - resolution: {integrity: sha512-Ua0JfNCnz8vQ8xou78+3YDmfIdo1AiHtIILGokb8UinmMNNmRom3e3GHXfeDNi+ksI+nZKLRQilhEOHdexjaDA==, tarball: file:projects/pod-calendar-mailer.tgz} + resolution: {integrity: sha512-PCWv9yJXNENF/5m8fAy6U2Us2Px55tcahzQ76fZJ0XaikHj0IhtkLbrUn+GPZ0+oEr3nYtA6LoEJ2Ow+FkyxJQ==, tarball: file:projects/pod-calendar-mailer.tgz} version: 0.0.0 '@rush-temp/pod-calendar@file:projects/pod-calendar.tgz': - resolution: {integrity: sha512-4YFUB1MJG6+cDaNcroiOa3rg7+CPnK26HWKsBsYWxWZm2rQDsLTwS7M9GJhSCZCN66rxW6Y9Ix7VgJ4bYqsjtg==, tarball: file:projects/pod-calendar.tgz} + resolution: {integrity: sha512-t8c7SYzZ+A5doqcHflyA6vFigX0ZM2PecqBid7A7WqFiPq9jTxuWyObqL91eqfuFr6C+2rjFnQwVNUWq2M94wg==, tarball: file:projects/pod-calendar.tgz} version: 0.0.0 '@rush-temp/pod-collaborator@file:projects/pod-collaborator.tgz': - resolution: {integrity: sha512-xfSgqEqsn5SKSvHasLUt4EyZ5OyKp68QUjBkyNp4GY/udGIU7BMurFz7A9E36HjMW/0/8Nz2eAm9b/EgxthMQQ==, tarball: file:projects/pod-collaborator.tgz} + resolution: {integrity: sha512-aXRRQwBxrL7/D6j9R7kAtWnpZCMOlf1Z3VmNl/YcV4HBPQXQNPDXULPiL1nmdy6aT17jrKB56Bt6iUSDjgVvdw==, tarball: file:projects/pod-collaborator.tgz} version: 0.0.0 '@rush-temp/pod-datalake@file:projects/pod-datalake.tgz': - resolution: {integrity: sha512-GyldlpXf60skJYCDEd7asj0wmf3E9NlfXdWlclNEop2JN6YU+2AjIq4CvdZBZwsBs1awJ8ToMfdj73Sco+fyeg==, tarball: file:projects/pod-datalake.tgz} + resolution: {integrity: sha512-iPAEPVLKLh0BP8/aJmv4AKyMt4b0fYAIFfJPa1X+uwlpPM04eDSNBcyeCPM1AQDJo473sIGU+xRQ6FjC4PjDBQ==, tarball: file:projects/pod-datalake.tgz} version: 0.0.0 '@rush-temp/pod-export@file:projects/pod-export.tgz': - resolution: {integrity: sha512-W61IK+X5R2r5SS3h1UL6GynG7ABXOcW6dPGkmdrb4IAh7+zvYxVFaqTL1ORYK5zJUq+kyf8SRMGlzaA+v/4W2w==, tarball: file:projects/pod-export.tgz} + resolution: {integrity: sha512-4p25nLpBxPOkysrwHzIQf1pYATka1m1/hvZD6gBfwetllotdmFxOvOeUkGUCk/fKwgsbK2FA3yr7gdOJGGJ2Nw==, tarball: file:projects/pod-export.tgz} version: 0.0.0 '@rush-temp/pod-external@file:projects/pod-external.tgz': - resolution: {integrity: sha512-TszvCm1U2CsnvnmNGSqqvYhOsRwh0uSn0dQEFVcXwyP12xTzYsLT5+Zlf2BVIy8B6pH5G3L/pBMTkZyQDezvgg==, tarball: file:projects/pod-external.tgz} + resolution: {integrity: sha512-MApLxTY2+EHlluhgWxx8ezePSnzPSOr1LRX9fxDTl7Fir4aDav/XqhfknTvlI8WHuVId3rzJu8bVQEHr9tCfWg==, tarball: file:projects/pod-external.tgz} version: 0.0.0 '@rush-temp/pod-front@file:projects/pod-front.tgz': - resolution: {integrity: sha512-YsdWbazewr8FnTz/r1i7zfxYGBM+MF9QnSEUGLCo7hHdTnQZvWhhOvZJFnnZrDZTGSpPMtmMUKjEsYy6hI1LCg==, tarball: file:projects/pod-front.tgz} + resolution: {integrity: sha512-xT2M1tq89LSlhxpWxk+lf+aeoi39JzyA3gCL82+tZVw/1igsf8Ae7F04iR5z7H5maaizMOkiFLJBS3spu9YlIw==, tarball: file:projects/pod-front.tgz} version: 0.0.0 '@rush-temp/pod-fulltext@file:projects/pod-fulltext.tgz': - resolution: {integrity: sha512-hLGt4bN05CGfaQsuZw9FZz+y67pvY9nsjBDYtJaqAH+fS3bMAWyxghkRVXDA4nGQoh/UMcq6icaPmCEOIYOOFw==, tarball: file:projects/pod-fulltext.tgz} + resolution: {integrity: sha512-PaDUc5ySKFtqvmPZ3A1cmlPBi4mUtzI/+H5LLI8UQr0wLAm0pF+G4xmJ6AhDkV1FMQqE0cBwek80UlL44+nLsA==, tarball: file:projects/pod-fulltext.tgz} version: 0.0.0 '@rush-temp/pod-github@file:projects/pod-github.tgz': - resolution: {integrity: sha512-DqxTb6VsPgOGexJ8/pzo7kTZA5MV/P08TgRhQgZl+EU6YMFYlilTjOrw/RQiUsD7xrNFMBr6mJiwYtmlrqHI4w==, tarball: file:projects/pod-github.tgz} + resolution: {integrity: sha512-EMLTmptkFgbFcUJ4UyucBGTpBg6N7tePxuRmPREz/s/t77SE3AgZPsovIMl9xEepy1oCGmtC+w8YBpBCA+pcHA==, tarball: file:projects/pod-github.tgz} version: 0.0.0 '@rush-temp/pod-gmail@file:projects/pod-gmail.tgz': - resolution: {integrity: sha512-W3oMsI168EAirl93mFRy/qu7XLGFeeI93PV7aTReGDD+GkDu9R4LjJLFAkfMUMpQA+KIqtNMyIpwhODmOmL8Ug==, tarball: file:projects/pod-gmail.tgz} + resolution: {integrity: sha512-xsImmCyhcKa/8jiAxL6k+NDEu2NdMclOxtubLrGfc9pXJ8Wxn9rfsy9Na1SuBH0O97DEml0hoDDosKr/1x6J7A==, tarball: file:projects/pod-gmail.tgz} version: 0.0.0 '@rush-temp/pod-love@file:projects/pod-love.tgz': - resolution: {integrity: sha512-QyDzi3UTOoBvWGS1ZlPRoh7Z2wtveyg29QHiexC/eBwMP/berpjyT1ZRZCVenZGzEckaSgqi8J+ONtGnrbZsXg==, tarball: file:projects/pod-love.tgz} + resolution: {integrity: sha512-LRTI7TVeaEu3dxyuEqyI3bFvLRliwHdt9N/WGtUQayvCg/pBzaCUK67m/O1XoWKnwRiOsrX4WSkfQn45+xB1yQ==, tarball: file:projects/pod-love.tgz} version: 0.0.0 '@rush-temp/pod-mail-worker@file:projects/pod-mail-worker.tgz': - resolution: {integrity: sha512-pAmn/fI3yZCOZjtQg+7RzayUJUvykF4bbHzDRI1o8JolZ+Wh+DcZMb5qnlo6AC2oXfA9J4onNPLTANyZ60AEtQ==, tarball: file:projects/pod-mail-worker.tgz} + resolution: {integrity: sha512-bS25U2+tE+81gGFm2wBebujB2R3FdTKII3bvsZGiYl1IFMkKW+obckgT3+H9nOMf43mEfW5yDaW1m6BOAxk1jQ==, tarball: file:projects/pod-mail-worker.tgz} version: 0.0.0 '@rush-temp/pod-mail@file:projects/pod-mail.tgz': - resolution: {integrity: sha512-zCxEHPeZn5t1DfJNzUspVY92AJfma2bRSF/Wl1RPtgjW8chSj4/bh6cidVaFLS/UhBie0BrUPKIjrofiUU5RbA==, tarball: file:projects/pod-mail.tgz} + resolution: {integrity: sha512-TDvO9kN32FbXtJYohjxf4B4zYBTtiDg9taTmyD9Mk/yYBEyW1shuvVgXqqeK11nbmYFrmy0kkrCoCWJjTeT01w==, tarball: file:projects/pod-mail.tgz} version: 0.0.0 '@rush-temp/pod-media@file:projects/pod-media.tgz': - resolution: {integrity: sha512-53oWL3o7btGcZio2ezQadNzQOQ3XjQ0Duzg5x0GZfk8h3wWgMp9F4T9R3arN3xsRFgFzbkbAaPTMkGgqu6V0sg==, tarball: file:projects/pod-media.tgz} + resolution: {integrity: sha512-tC8RMEvA+IpZLEdfQzg06PsIlbs4YtggfLmuXbj7Crf7IIO8IKg3+eG4rLzIYPDTxPpcr/Vn7mwtxqf3yCNXcg==, tarball: file:projects/pod-media.tgz} version: 0.0.0 '@rush-temp/pod-notification@file:projects/pod-notification.tgz': - resolution: {integrity: sha512-ttkeWBu/a3F0/JI5+lKycEUln4aUuBwaBm1O3pGMDtkSq6rdhbeCa15v7PD6ER/d0gWp/Ll6YOf0y+2XkEoeJg==, tarball: file:projects/pod-notification.tgz} + resolution: {integrity: sha512-gkgRcuESZ0/a8jR3P1/Q9JidhSYa6fdlYY2ukNqtgLYTte+X4h6l0BYvNuqGMPmjHjKHpsnd46P6ac+R84b6Yg==, tarball: file:projects/pod-notification.tgz} + version: 0.0.0 + + '@rush-temp/pod-payment@file:projects/pod-payment.tgz': + resolution: {integrity: sha512-TjDHnMpZOhcDesWVZiVL4s0c1/uv+pDhJyscu4H+z6CU9EstIeOzqQLuZBZxZIqWbDoTeC4ic2mNMhIUPaNjSw==, tarball: file:projects/pod-payment.tgz} version: 0.0.0 '@rush-temp/pod-preview@file:projects/pod-preview.tgz': - resolution: {integrity: sha512-x7xIxT5ASQ04ywu5koeBuPU9WhTvpar8ARid4E8QrNrFktOzNE6DsafR+8NJnuDarEL0Lb7oGs7gFcg/nAyhdQ==, tarball: file:projects/pod-preview.tgz} + resolution: {integrity: sha512-qayz38ZFKViA4Wy4rFrTaHp+K9lVfc/pxuaOm5ko2nL69NV+XEeYw/nH5kGjDR0NrZ1dDFYYhFH2cWFv9sPC5w==, tarball: file:projects/pod-preview.tgz} version: 0.0.0 '@rush-temp/pod-print@file:projects/pod-print.tgz': - resolution: {integrity: sha512-wbyHi4cB/sz9OTG1IyWNB3rdDrZDURx0mY1aPT/0qL3lQZNo6G5R20vIqvEyBXaZXSg2S2Ukh+EBrGBuSuhJBQ==, tarball: file:projects/pod-print.tgz} + resolution: {integrity: sha512-E33G5L8ZwejluXJ1dDgU08pfMCXzdo+VF7vXG2DGV8yKOaMO7FniMcHeIz65lShW6ThnQtexVVpNtQdKzVNlpQ==, tarball: file:projects/pod-print.tgz} version: 0.0.0 '@rush-temp/pod-process@file:projects/pod-process.tgz': - resolution: {integrity: sha512-PV2Y0F6BIhzgmo8c47x/OtIoDdGxZOEEW1v2V4qNUBHoIrk0e/9J+scm8cfnRM/gRRGb4q+MBgZk7yNchRZgIQ==, tarball: file:projects/pod-process.tgz} + resolution: {integrity: sha512-0PeRV+dun0t4veAMY/hVzOLyO5BsFqfI0PXofIV6ZvKdtjUC6+BHz02LV4gDWoZ1QbRUq8BfvxHJ511hhdTwWg==, tarball: file:projects/pod-process.tgz} version: 0.0.0 '@rush-temp/pod-rating@file:projects/pod-rating.tgz': - resolution: {integrity: sha512-cNNIpjxgOh7/NW7KMFAq0fPDJc4FN4TpfMglzoaTiPJGI5wubN6dfnfUVCXAHnu/Azi9iKrs/BwnzXZz0TdPVQ==, tarball: file:projects/pod-rating.tgz} + resolution: {integrity: sha512-KCn7gAkqOE1Np96C710lZvHh0cGb3fgQwzHSSOk6aTkjc7vvFmgt+a3LsBEThwEe7B7KXVvquqQf90t97yHnFg==, tarball: file:projects/pod-rating.tgz} version: 0.0.0 '@rush-temp/pod-server@file:projects/pod-server.tgz': - resolution: {integrity: sha512-4hhEU/F/t2+X6+UDS6AsQ/iSEGHD7l4xsE4ppkbDBI68WxCp6MlWFKMiiiZGFnDTjVOdaCwRZuDCxosfTvSblg==, tarball: file:projects/pod-server.tgz} + resolution: {integrity: sha512-g5SGkb4hyTPgfitAusfXnq0bCSrPCUjH5/1L9lZ7IyROBU7NsiKCe+z+F3Tu9sYOHJSTAboC6tvhrITerOGOwA==, tarball: file:projects/pod-server.tgz} version: 0.0.0 '@rush-temp/pod-sign@file:projects/pod-sign.tgz': - resolution: {integrity: sha512-nvrkaAwSHhkNfmzGaZlcrlZVowcLhir+eRJwSePWd/ggmJ380T86a+h9/qNFCoNKoZujNl0TbBH0A82cC8EzoA==, tarball: file:projects/pod-sign.tgz} + resolution: {integrity: sha512-bnhm7Kr5Onm5Xtlib5kPVsUvJ7SdNurXzL7OLz+G2nuBblkK8YivpXYxHvQv/lQ3+A4lX2WPbOZsvV6BaiPbfA==, tarball: file:projects/pod-sign.tgz} version: 0.0.0 '@rush-temp/pod-stats@file:projects/pod-stats.tgz': - resolution: {integrity: sha512-bLo9WYfpohxJEzu26+qFPCoN58y7902Psb+aqSldOz5khaf2HAfxXX3WoSJMus3ClXDFD3Gopfzgzex461UkXQ==, tarball: file:projects/pod-stats.tgz} + resolution: {integrity: sha512-yOkXFYbUFVtSD/w/NQugN1b+90O9teAINJEieORlI6kelZytOyDx3j3YJCmf9h8gwHJz+Zs515/bjWmV+tyaeQ==, tarball: file:projects/pod-stats.tgz} version: 0.0.0 '@rush-temp/pod-telegram-bot@file:projects/pod-telegram-bot.tgz': - resolution: {integrity: sha512-AT89z1+hJDYuDqALcCjchrnR5sSqQ9e7OQEytx1SUga7QNzH0bHa2MDUpkr8yDoD7zt+j660O4jVD1GklZGhlQ==, tarball: file:projects/pod-telegram-bot.tgz} + resolution: {integrity: sha512-IP6LDKt1V5CT1rFopNl8Dm8VBygnlZ1gbvHbVGRXetx9sgs7bDYyFtM4O0uFicNq0gA5ufeuUrZpd6/57Ca3FQ==, tarball: file:projects/pod-telegram-bot.tgz} version: 0.0.0 '@rush-temp/pod-telegram@file:projects/pod-telegram.tgz': - resolution: {integrity: sha512-sEhGoefGxVsm89JYDqmPzNZi/sb/9tVjnHzKcZv8z6v2xsmGv5luCJPW6svpfhnL9CAqj47Z9lJB2E7ZLTXx7A==, tarball: file:projects/pod-telegram.tgz} + resolution: {integrity: sha512-h5WL8sxrpNhcQSGXknHhiZh9FmuYdauHd7OMXPlpBPJcfBF3tt8FX7IixH0WCNoMCcG//wvc2FgNthHEZNVOyg==, tarball: file:projects/pod-telegram.tgz} version: 0.0.0 '@rush-temp/pod-translate@file:projects/pod-translate.tgz': - resolution: {integrity: sha512-LIEq/2kyD5MZSuV6awaFl60Wfh9/pdauC62SYega2OQEyHU97Ilj/cvxvtV1ZtXToaGflMciGR2IZoylXjDYgg==, tarball: file:projects/pod-translate.tgz} + resolution: {integrity: sha512-wVadoKdG24wuzuMEaNZuq1tf5Wxv21L/RokTKaSXwoyAytCvlq9XAOybITx66fzlbID0ZgbIfxGTtqkP84dNng==, tarball: file:projects/pod-translate.tgz} version: 0.0.0 '@rush-temp/pod-worker@file:projects/pod-worker.tgz': - resolution: {integrity: sha512-EZuQ3XnvQXES/Ms8ulrkXMd7qL/rOFntV46qJY4PUTqnftWXmba/pi7K0Tp3AgxvauXU0W9/fC+6TKw2v0ttQQ==, tarball: file:projects/pod-worker.tgz} + resolution: {integrity: sha512-Nt3X8zUmR6ipuUxMI16utbehSsKAxhCgb0+GxyFJCf5xzAsTPrRd9HZShp5BSbggYO8CHlaUnP+ngYyA/3ohNw==, tarball: file:projects/pod-worker.tgz} version: 0.0.0 '@rush-temp/pod-workspace@file:projects/pod-workspace.tgz': - resolution: {integrity: sha512-0qAjWJDQGL1wicw5mo2YrlYhhDcN029x7/J1NiP/09FtAptQU2clw6O75am1f2oIQv4nrtiXa51Ut9HtktIL2A==, tarball: file:projects/pod-workspace.tgz} + resolution: {integrity: sha512-hj35GrjGbb03hbKExFa4G5TpRTAUdLLg24Dsc3eJC0RseONuo0yZY7w/RPpPPhdfYfFXOsy2HqNBG7gtw+cUow==, tarball: file:projects/pod-workspace.tgz} version: 0.0.0 '@rush-temp/preference-assets@file:projects/preference-assets.tgz': - resolution: {integrity: sha512-Cuv9WsOxp2XYJP0DOarBvsgkqmQwXeUut8eLYvNdHyFP2yqFq/jFKz2Ue+wS1OrqpCGOUjpYdWOiCu2d8t1EHQ==, tarball: file:projects/preference-assets.tgz} + resolution: {integrity: sha512-jZHyI17CUpWJbillai3p9zDGCrXrs/7EYepZPMxMBWoZRHsdSU12um/0zNQDdPU88uaHiGmTfvrvkgeo+xADtg==, tarball: file:projects/preference-assets.tgz} version: 0.0.0 '@rush-temp/preference@file:projects/preference.tgz': - resolution: {integrity: sha512-tb/ZKWXsnSAYJ9KdRxe3r++6ryK1a8+8AIFgTskjQupZlKwVSJuNkR7Qy0cnPkcxOXcKhzVpLLPzzM7crEiDXA==, tarball: file:projects/preference.tgz} + resolution: {integrity: sha512-MgFNYOeuox+rtyf9gtuRu64+qwCWbOQJpNwuJOHQlYuecC9v2XyrtayphtMAC4eM2JToKLLnyfAKm3yLkXxJlg==, tarball: file:projects/preference.tgz} version: 0.0.0 '@rush-temp/presence-resources@file:projects/presence-resources.tgz': - resolution: {integrity: sha512-Nui+3V53rAPUncN7LSu9ijJ+xMIL09hTVV7ZFp+LYdIWDtAJn0toA1VjcaFhr1j0lkba2PhJSaxx7OtuNWd7Yg==, tarball: file:projects/presence-resources.tgz} + resolution: {integrity: sha512-CuGMrY+6/k+sj2hbrnN54hHGdtxCKYgWoRzA2g3TyTNS/2UVv63b0rVOgP5Z2BINWD3F4npepE5+WIB/O0wV+w==, tarball: file:projects/presence-resources.tgz} version: 0.0.0 '@rush-temp/presence@file:projects/presence.tgz': - resolution: {integrity: sha512-VZ683ppYKNETotpm5Qk/HjpOnu2umfdUjyZ8G6dvIkic+ny0Lwyl8Rc4EpyntcojzuxqyajZoZGfkKI+Fu96WQ==, tarball: file:projects/presence.tgz} + resolution: {integrity: sha512-bgJBZHQWUPbu9NGNEbAoVJHJNEBC9eCiJJcBazql9NIWuCIPvsBPs6DBO/vKa03OD8Oiw4mUEJ6116H9vaM/UA==, tarball: file:projects/presence.tgz} version: 0.0.0 '@rush-temp/presentation@file:projects/presentation.tgz': - resolution: {integrity: sha512-9Er9MoexMKYRffK5ZmCj7PgSmUiU3lf2MJGfAIlZFukwLxFh78OKtDNiIHT9/sPQAbGGXUSHKFiotL8L6UPexw==, tarball: file:projects/presentation.tgz} + resolution: {integrity: sha512-7xhUkG3JI35L8b94ut9V7zR4SRXdYss96tx73lTCp2TGLoEni2NWWTeGgPsOCywyqzjcHjLTBk/CEisEil840Q==, tarball: file:projects/presentation.tgz} version: 0.0.0 '@rush-temp/print-assets@file:projects/print-assets.tgz': - resolution: {integrity: sha512-rALo0C3/ckCGBSGahcgD3YvMplyOvPEuuaxcAUkS2LBIfNJX8BL0Y+hvs5ptm09dJWeY6KvcXra/1Vw56Rkkdw==, tarball: file:projects/print-assets.tgz} + resolution: {integrity: sha512-vMIMM8okZTKr2jwW1MuhHOenRP6tPezA6xwItm3ebwm+Lvk7kLW0bIxwAdjoOrXWwPdhWnr0Y0fHTRITbIefOA==, tarball: file:projects/print-assets.tgz} version: 0.0.0 '@rush-temp/print-resources@file:projects/print-resources.tgz': - resolution: {integrity: sha512-7hJOAAcG1gxgX9iEgLMOHr7ICiHJAEv+OJm3/mdlGMNoAtJvhRrO+alxAU5HoJkm3DIjpF3V6LY4j+zlS/z1Hw==, tarball: file:projects/print-resources.tgz} + resolution: {integrity: sha512-C6Zb24pFuAiWJhBRqa2YjMOaq8qTxTRlPVvGNVcRlEGBvP5X0bPP+bQnIUIbh3FFmbJGnbAc3TbMSG2jGCMa5g==, tarball: file:projects/print-resources.tgz} version: 0.0.0 '@rush-temp/print@file:projects/print.tgz': - resolution: {integrity: sha512-g89qLmqSCPQ4C5c+uS08vrGQpMKW0RHVESD92XZW7N/4iXvtTwZ7ETxRHil4aADSlezSDdYf4h+c4/DE4EuBaw==, tarball: file:projects/print.tgz} + resolution: {integrity: sha512-xMjvCB2VLZ4n0HgVDi6/IBLyYD8V7ry6ozTz8HgOuH1YgRX4OwC9P7KYp7CWjgESmdBCs71N1OZ2h7TmFk9RBQ==, tarball: file:projects/print.tgz} version: 0.0.0 '@rush-temp/process-assets@file:projects/process-assets.tgz': - resolution: {integrity: sha512-0PNiGdeoMRrXcci2BQhP5jSMdoEIvAWELJIMI+yXoz/Hi/YVyGlGM6fzJ6ENtUd2yOqEjyIPz51kdXuMVO3Ynw==, tarball: file:projects/process-assets.tgz} + resolution: {integrity: sha512-BwJnNtGzA8VxJarCwHszaD2BmqcBZqoq1embTtw4KOcsNqoGG9D8az2sOvtwJYvw8q1p9+/Cnzoj5U6C2lLyiA==, tarball: file:projects/process-assets.tgz} version: 0.0.0 '@rush-temp/process-resources@file:projects/process-resources.tgz': - resolution: {integrity: sha512-uS6m4IzSFINUktwn5+sIpM8bYVUrjFwis+2moK0imxemLCW5RdGxZbeQgqm8ZMi0PRJYZoMtVTMlx1wz19rQFA==, tarball: file:projects/process-resources.tgz} + resolution: {integrity: sha512-sAyrHxk50B6qsrn6GIvoZlfN8XpDIhflkP52JHz7HZHJp3SkAGL89sYXlJ+jgETNHmGZT1ym9gZ8aGdK5QJN4g==, tarball: file:projects/process-resources.tgz} version: 0.0.0 '@rush-temp/process@file:projects/process.tgz': - resolution: {integrity: sha512-1PYa/Qcee+FMku9DT+3DrBwEjqZ7i5l8BBjKyKIKlC66m8323BOUvOfq3WBP8GOc190BUmXnKD5GXlwpnSbFsg==, tarball: file:projects/process.tgz} + resolution: {integrity: sha512-wXZyvwFG7lx6Sxl//bTycsEfO/c3cxK/SRhvks4Yv1rz60W4wyLVsPJ+xPESK1LSsmuXlABtdi2zvJnI3CSj8w==, tarball: file:projects/process.tgz} version: 0.0.0 '@rush-temp/prod@file:projects/prod.tgz': - resolution: {integrity: sha512-Dr5Ru86sKttYzVRonISzI9/RneTaTS/3nKtBi5hxAgvM2/6FLYkzscOr2S/d1N4qFLVicM2J55HlQ4H+oZbgEA==, tarball: file:projects/prod.tgz} + resolution: {integrity: sha512-tyjJncR/d6D0tZpEcHJo4yOZ3TArK2ONXAqgM7hJsRQp+FtjGrlfI7Ix384ygXCisL3Oe3bsqm/uP3SpVzTxVg==, tarball: file:projects/prod.tgz} version: 0.0.0 '@rush-temp/products-assets@file:projects/products-assets.tgz': - resolution: {integrity: sha512-ry6qHZNQ9gl86mmFUsOKC1Ed91OrlQXC1DimLU9wECuTzciGIbYf+XyW93fk37MdWQbXIZN6Q1gc2YuUZq+wcg==, tarball: file:projects/products-assets.tgz} + resolution: {integrity: sha512-jAVjofB4mgKnO+sAd06WN/mu4ZeIXrhJoDXIna5jobnKbmO55KYoRCVXjDqHnsoarDNlYBHF1sJvFb0jS+EVNQ==, tarball: file:projects/products-assets.tgz} version: 0.0.0 '@rush-temp/products-resources@file:projects/products-resources.tgz': - resolution: {integrity: sha512-54Ho5+7zPmTVlFbgVi0L3eD9QD1yjOPHIiPfM14AE/DrGPnJxfflP+pTqqu+2BgZd4a4m/hlSZgAh9GY9xDtYA==, tarball: file:projects/products-resources.tgz} + resolution: {integrity: sha512-6YnIGGztUNS8e8xMqpzrkMKuT5Se9PJzPM3b7E3sNCQIPT4R1q0aqDsKNCu4Ne1T2LrbIdg3fG+HT5ea+ndIiA==, tarball: file:projects/products-resources.tgz} version: 0.0.0 '@rush-temp/products@file:projects/products.tgz': - resolution: {integrity: sha512-Qk9u21+JD4MQYTEuQ0gpNIysZWIHJicWVBMyHMq7ExVKvSNB3Q1HJjCgmBWr8qQU0RL8r7nnV5nHeJBsNBqAIg==, tarball: file:projects/products.tgz} + resolution: {integrity: sha512-5d/yAdjzfWvHgoZl+3ib9+39te9LfKYmMJ65HYQ6WHpveaLlhRHhDoZAWza2d6FU1kyLMMQviswcT2o8IJbAGA==, tarball: file:projects/products.tgz} version: 0.0.0 '@rush-temp/qms-desktop@file:projects/qms-desktop.tgz': - resolution: {integrity: sha512-quQ+YvblpxsgsKSYxbCzYgHpdWdnUN1h2/21f+yg/QMFus1zFzW7JyHvZLfTiYrfJkS28WO04t/NzNwM0oDhrw==, tarball: file:projects/qms-desktop.tgz} + resolution: {integrity: sha512-QDqI3XvTJrJ4ZpsNcI+TVtcpgW81Z5DfmiLszEA6oHyixHcl2mOJmfQjECXwraOgn5x74Cd+yjOl/LxJ0FQWYQ==, tarball: file:projects/qms-desktop.tgz} version: 0.0.0 '@rush-temp/qms-doc-import-tool@file:projects/qms-doc-import-tool.tgz': - resolution: {integrity: sha512-ZVUemlwM0tXQ2tdZE8gGqwICVie42qeh7ivv/qd2PS8yyApPtP/l54MUrFnB+CrbjVebsGNEZKBK5fTI71Hmyg==, tarball: file:projects/qms-doc-import-tool.tgz} + resolution: {integrity: sha512-w7ykY/TNgRtdwXfcM0xdP6GEQdNH+4Rum9G72O6VGLQa3clss41QtWF/ZMK8fQ2XKHvUaerCYIhbS6gN32Bv0g==, tarball: file:projects/qms-doc-import-tool.tgz} version: 0.0.0 '@rush-temp/qms-tests-sanity@file:projects/qms-tests-sanity.tgz': - resolution: {integrity: sha512-ZyijvixWu5u+ZVDyvni6qtZhdJvIGzrYbb/JF0awecYBfS2DnlYBSYWila6KvXavdGsfTkum6/WnvXCJ1DIxOg==, tarball: file:projects/qms-tests-sanity.tgz} + resolution: {integrity: sha512-pSFrgOvwP0GvTroYbkYGyhrlQrMG5P4kvH/fcTKFgD4gn2p1UrhsUI24TsvHEmvdh2yGA922nkgwCcMYQoCFtw==, tarball: file:projects/qms-tests-sanity.tgz} version: 0.0.0 '@rush-temp/questions-assets@file:projects/questions-assets.tgz': - resolution: {integrity: sha512-DvuRcO5arDJIvKaKftbdOVH9Ag40TfFbarvjA43u40+G2HLUFmKZYN6xL/27kyvD3JRNOKtV13PhjLi9WQfSvA==, tarball: file:projects/questions-assets.tgz} + resolution: {integrity: sha512-ZPMB8hiSeeilMyUy4uOMx5V7U3it6wRjzFkxryE8TlmeDHW0TsjnXi1/TZiQxvgW65OFp5o8zLR4EECL/00C3Q==, tarball: file:projects/questions-assets.tgz} version: 0.0.0 '@rush-temp/questions-resources@file:projects/questions-resources.tgz': - resolution: {integrity: sha512-9KqtTGqcQ6BYgFOFvY9aVFB91k6PDIcrktjpBcRA7WQLw/X0bebhYcXnGddP/V7nwcKpMn8rK8xNsuBIdFHOgg==, tarball: file:projects/questions-resources.tgz} + resolution: {integrity: sha512-sCk+ZMsCHYQkcBDK5d1vWjYdGECb3r1ivDnVnbMowAFJs0D7FfUYwq6VgqCCTPvC8iBYcYwF0S5yA/L01WjNPQ==, tarball: file:projects/questions-resources.tgz} version: 0.0.0 '@rush-temp/questions@file:projects/questions.tgz': - resolution: {integrity: sha512-rw7USYNI3irzsTtsBQbN60aQKMTEvDrttHwmZevcZvqTzIIKwDh6hsriesgbDzmO9zaGdThhB9U1us7FYIfYyg==, tarball: file:projects/questions.tgz} + resolution: {integrity: sha512-E1oPML5bn4DoP1776DsMqfDUQxIY6yoCvAtu66w+ANX/syJHuOB18k/wUD8ySruIGnzeKZuLY+or2dLHlPD3Kw==, tarball: file:projects/questions.tgz} version: 0.0.0 '@rush-temp/rating-assets@file:projects/rating-assets.tgz': - resolution: {integrity: sha512-y1w1PgsjPxrQaqNSVWvS+xVi2riqBHD5P0oUclz1r6Wx61qWWAr34ZFlab05SLBGgzoKJQtwWm2feI4gRzq4SA==, tarball: file:projects/rating-assets.tgz} + resolution: {integrity: sha512-JPx+d6HGhWSBj3YoZMQKju4hw2BwRR3TedLzZn1MeQh2qVw+AOYSHqLrkcnMoa44YLERq22fRNQnj3XDSuweWQ==, tarball: file:projects/rating-assets.tgz} version: 0.0.0 '@rush-temp/rating-resources@file:projects/rating-resources.tgz': - resolution: {integrity: sha512-rmQ5JExmjsPqyQZxNzJyvAE2FfPj7EIGL/nJxlYXHOMrE37hM4qRE1C94ruCWvIk6fSrrWPCuV6DQbDxHozR5A==, tarball: file:projects/rating-resources.tgz} + resolution: {integrity: sha512-b3vz5vwNew/iiC1sEihXymY/W955jdOyIj5AsNo+9+dXQeyb/cPY6L5Nyexrt7I8t0qPOVPDiPX2IcUbREcafA==, tarball: file:projects/rating-resources.tgz} version: 0.0.0 '@rush-temp/rating@file:projects/rating.tgz': - resolution: {integrity: sha512-9AdSvOp0vFCayFxS3Xj1FxuS0c+JEKwa0dqfBELMcwj8JrkwsyY1ww3z1EVi8bR8pWbgx/Y270K+ARnRsq94hg==, tarball: file:projects/rating.tgz} + resolution: {integrity: sha512-mucPpQXTuRLuzMioDm//0aiKBnDirkj5QEfc/FjqEx2HJkkSVXWUmWccLWVohxdlOeN7JRfu7hbXwdQPPfulCw==, tarball: file:projects/rating.tgz} version: 0.0.0 '@rush-temp/recorder-assets@file:projects/recorder-assets.tgz': - resolution: {integrity: sha512-LHtdGrH8P56yAi1v+dhqDzDUp25Fvv90nwhAiBpt5VjcT2tl7bhXKfCb7OPo9AqhndC9zjQmSnkiTNZaAen9Zw==, tarball: file:projects/recorder-assets.tgz} + resolution: {integrity: sha512-YSXTu0o/0YiDQBgVuEaXbNvn4oeIx4hsrsOgoEjBy7AU7x119mZHqtLRDsqr4NouGQHpXl4m8h1UgUPkBmGGig==, tarball: file:projects/recorder-assets.tgz} version: 0.0.0 '@rush-temp/recorder-resources@file:projects/recorder-resources.tgz': - resolution: {integrity: sha512-UIKoM/+DFY7FSD+2QR1SCTxF+f3B7U9J5XCbirIb31q7OYS99F1YAM8p98A1A6T4hhjEIrWY3W7kpOizaJJgEw==, tarball: file:projects/recorder-resources.tgz} + resolution: {integrity: sha512-UK8UKvd5iQKg/5yCDI5pwKVJquvYBGmCyFA+ODoQsaTNm7FgSP1iyehZ1e2gAkAqssg1VssQIH+7hyDksJxKew==, tarball: file:projects/recorder-resources.tgz} version: 0.0.0 '@rush-temp/recorder@file:projects/recorder.tgz': - resolution: {integrity: sha512-x9UZMVF9qdzbDQFXxbWkBpa9vPVPbFgEmtKptWAJgiS1MeF5Z/XT1ufUwDXsVgmm80M8EJuMcPgSgjipeBpNTA==, tarball: file:projects/recorder.tgz} + resolution: {integrity: sha512-XjCsk9awFkc/eh9C7eIpmkr3VmDayak2I3U9jnUdw4D6YnjbX03nYWrjeVf9+lrO/VzwFopXHyeOkZ0v7mxG/w==, tarball: file:projects/recorder.tgz} version: 0.0.0 '@rush-temp/recruit-assets@file:projects/recruit-assets.tgz': - resolution: {integrity: sha512-9mKPcnTUy2+NoyM3coec7uX2qTFA/jQkUWGRIh3MU9PU2l2jfOEnVK9+DGaig7HHTBbvPNxs7SaiF9+3rAqRmg==, tarball: file:projects/recruit-assets.tgz} + resolution: {integrity: sha512-VOrhq+7PMdXo6P3W/dvehq0X4mC7gsfR6ItT0qN0Zw8LHNoSD/nqx5qMBg/DNAwpisiW6nNhTQXIV5PIMowb6g==, tarball: file:projects/recruit-assets.tgz} version: 0.0.0 '@rush-temp/recruit-resources@file:projects/recruit-resources.tgz': - resolution: {integrity: sha512-jOs5gQ4ZHtz+NYxxZVXjezZSWf7d0Ak79V+AB5Em2xB69lnOD3L5fJH4re8SRDCf4Y+ASOtpBBvkd8w4GFOSBQ==, tarball: file:projects/recruit-resources.tgz} + resolution: {integrity: sha512-nPrhYNuZSxI5qSr9acJe4WymOQ9JQaEldqfCEdDrvtEJkPN+iYh25U9xoPZnvIPEpF8BXM3sVZtQys38VUCX/A==, tarball: file:projects/recruit-resources.tgz} version: 0.0.0 '@rush-temp/recruit@file:projects/recruit.tgz': - resolution: {integrity: sha512-k26ZCXJ7pdVAwu8n8OAezR9aF9gOqxghgK+SenLV/JGV9DD26+wRY68xXFnSv2MBUAdQUbxa+r37NbrBr5xxSg==, tarball: file:projects/recruit.tgz} + resolution: {integrity: sha512-lmFeaP2VUdjhRvE96i+VYzIDO1y7DRxi9kHxRIOcL7FbCoJiTgYQ8YUzBL03gXDdl2VcICID9FNGUNao1dT7Ag==, tarball: file:projects/recruit.tgz} version: 0.0.0 '@rush-temp/rekoni-service@file:projects/rekoni-service.tgz': - resolution: {integrity: sha512-08x60XP0JkYWLipsb91jngYrWRpNxUoHvcIARP2OwsJrXgUt+DHmwpSmZGTzBDls94oz1QlQifeCF6fWE3I2Ng==, tarball: file:projects/rekoni-service.tgz} + resolution: {integrity: sha512-7DLS5knkgrY6RfFltR7K8xZgxUuivNjpHHHDAhA+mM4kqfI96X55fIyDU5Yt/obZmw/2nR5VI8qyjm0T+KNxMQ==, tarball: file:projects/rekoni-service.tgz} version: 0.0.0 '@rush-temp/rekoni@file:projects/rekoni.tgz': - resolution: {integrity: sha512-zgHb7CLptGyDFm+q8zfWz3pQfQiIeBF+O6XXclOgc49Tt/xtQccX2xXab9wXBxSO1q4jhIkp5jGO8svXRCgSkg==, tarball: file:projects/rekoni.tgz} + resolution: {integrity: sha512-kUIGqjBbrs4pKF1NIyWs1JKry/wjn3Vyxir07bF9pQd+XnDVlXtxL9Vd/QAKE9VxujytUF/oVSwVYMd+GlfBOg==, tarball: file:projects/rekoni.tgz} version: 0.0.0 '@rush-temp/request-assets@file:projects/request-assets.tgz': - resolution: {integrity: sha512-WjYF6nzs/+Saz6D12sjdFxIPBGE1pE2wn6wCqBiGc4t5NZRzfhxSpptp2OfEuPzzRGW8XwWoowahwc9/qzvZkg==, tarball: file:projects/request-assets.tgz} + resolution: {integrity: sha512-ypt44/WVSEt/7Db2yXBGv4AjFy96P53wh8eesTBncO3vYESRQQIZ++yB4L25XiE6Fo1qo/sJ6G3fFB1Wr0TAGw==, tarball: file:projects/request-assets.tgz} version: 0.0.0 '@rush-temp/request-resources@file:projects/request-resources.tgz': - resolution: {integrity: sha512-9Qoc5DhkT1zkQ7c9rIDfJMMiGXftj9DIz3NGAnVo+2mQ+JhJ0b/NUaKW7zvN34il0NKZ3PKJ9+2mtWWtMSEPoQ==, tarball: file:projects/request-resources.tgz} + resolution: {integrity: sha512-P8+sqrYSNlQRWqG0/2OBFiECOgTAjhnXItQBqkXZS+IqnZr3VwAhXaUu4yiU55JAYmu0610KNhi0258YQ5xnBg==, tarball: file:projects/request-resources.tgz} version: 0.0.0 '@rush-temp/request@file:projects/request.tgz': - resolution: {integrity: sha512-EtTzOyC01u5fZ1t8LmbOq/SBqd6wDMuebXrwvvK/IOyVXcOqbc3IDxag6xGGJVGzeFxzoZ++qeWDJjNz3UOxOQ==, tarball: file:projects/request.tgz} + resolution: {integrity: sha512-PXdDbUj2m59BglNL9RZjancmji/FVvFalCNNj4gy0HueGvASAbbWmdivoMkDW+hLuO03ub3OKnE/mpmIum58Mg==, tarball: file:projects/request.tgz} version: 0.0.0 '@rush-temp/scripts@file:projects/scripts.tgz': - resolution: {integrity: sha512-B+qKM4WpYwsC8qmYQcCvciBnwI9yaNw6mYzYyAtNYoR/vZaCXGrgXMttUm9OPAwnL/ogLAddwEtbHCpF6HgdKg==, tarball: file:projects/scripts.tgz} + resolution: {integrity: sha512-9lPXICxVhYO9ZvewnLAMQc37stk15edjlRMoZAu2RTtiEEEdV8ejUDtyyS6TgrGf6dkMLjVJVBCfZF1PULwPWA==, tarball: file:projects/scripts.tgz} version: 0.0.0 '@rush-temp/server-activity-resources@file:projects/server-activity-resources.tgz': - resolution: {integrity: sha512-E5jErW0X19ukQ7rPgjE5ZqlLEmb91kqta48iaDWKbJAhnUnoXqsoLDIsMQVBhzUb9XoT0oUuHgLnfrnSOlfKGg==, tarball: file:projects/server-activity-resources.tgz} + resolution: {integrity: sha512-0vq1jB4UPw32UnOHJ736LJ8KErshtIFTzAj3ph4NaQePGCAOjU6kRmOdGyOmRaGUny4k/CIhE4tNToUej4XXkw==, tarball: file:projects/server-activity-resources.tgz} version: 0.0.0 '@rush-temp/server-activity@file:projects/server-activity.tgz': - resolution: {integrity: sha512-ofUHRzxIYFvf5NExeqi4RmsfhPjdVCrQKBBU3T0/35SIz2545G71/sJMqkI3kEdbCEcYRMRL/1ZSbzsGjtRlHQ==, tarball: file:projects/server-activity.tgz} + resolution: {integrity: sha512-6RPcwa/3kmeTtA/sOHahzFVO/Cv+kXhKFexK8Ynwc8UsYj2YQMIIp2idxsX2FNBCCHPDwg6JPwhyc4dB2ooSQw==, tarball: file:projects/server-activity.tgz} version: 0.0.0 '@rush-temp/server-ai-bot-resources@file:projects/server-ai-bot-resources.tgz': - resolution: {integrity: sha512-wlxuZp2iJfcJIFhUo4tanfIqZhrHNoGcH+KcpmLw8TeSOmQHLUDpNgnIM8MhFX8iR8D/iEDFtnE5bb8I0Rajlg==, tarball: file:projects/server-ai-bot-resources.tgz} + resolution: {integrity: sha512-ubn/NJNkQDGTIfC+NDnhMHJoNyHTMMsyXEWTtUI9FWqkosYfYXGSSOrTX0OBS3xB3X5lI0VCc63kyZXXIF7o3w==, tarball: file:projects/server-ai-bot-resources.tgz} version: 0.0.0 '@rush-temp/server-ai-bot@file:projects/server-ai-bot.tgz': - resolution: {integrity: sha512-CxoAyuZiAfk9XdJ1oPaxXoIlcwjElVqBPYrB7FeOvi0QfKJmXybidPWDeQQybsgf0mtKyzzkP/6qI8r+BNeV8w==, tarball: file:projects/server-ai-bot.tgz} + resolution: {integrity: sha512-N8FZinSM5a04X0RmdMXcn7F4F6nyx489+8gRktJX7YyR16R7z0ouQdPqRbYRwMOY0lLxyp9yjU2TNnuUtvECCQ==, tarball: file:projects/server-ai-bot.tgz} version: 0.0.0 '@rush-temp/server-analytics-collector-resources@file:projects/server-analytics-collector-resources.tgz': - resolution: {integrity: sha512-Ewze6BAfrnt26Zuowo5AeO3BkxBe7cHiSFHmFW7TnM9ozOJJ7nEgmmlCR4cBTxY26cmOOm8OW3o2XUNOSSvukw==, tarball: file:projects/server-analytics-collector-resources.tgz} + resolution: {integrity: sha512-ow2uCQ4wlTaQukq6AdROYB6FUO2zdIOXFEO/00WuCdRk9xFWiI2UkIDFr6z9Rn48i5u+BWfgLT6SkyVbGIjJvA==, tarball: file:projects/server-analytics-collector-resources.tgz} version: 0.0.0 '@rush-temp/server-analytics-collector@file:projects/server-analytics-collector.tgz': - resolution: {integrity: sha512-fIoyPnlcN8DC9s04HSp+cukFu8gFr+0y4OYuyYK5f7SBFL6Mr/tTY+kPGAQbOW8apBeH59Zrej/Ipqvb9wgN2g==, tarball: file:projects/server-analytics-collector.tgz} + resolution: {integrity: sha512-T/hm/olYQYSk/DE4arptr8BnciorAAxnUqSIX9VWcCXtXzcXayc5Wb0IOQJirtUWsqtzCiBYj476o1rUBpN3Qg==, tarball: file:projects/server-analytics-collector.tgz} version: 0.0.0 '@rush-temp/server-attachment-resources@file:projects/server-attachment-resources.tgz': - resolution: {integrity: sha512-XoNex//thLAfokVS1BU8qGl+2zvdBAT2tYFJvSDjE1nU5I0fSh0Rrwyp8VnJ9DQdGDK85dw8Tj15Z8wuxRUstQ==, tarball: file:projects/server-attachment-resources.tgz} + resolution: {integrity: sha512-QHIXlQ9mO+AxCAYqylhwuJNoMGzx/sN37XDjCMoN/oa+fRRgS8vLuC45WKTK5OZJGkYMTH5YUe2mP9WynBphLw==, tarball: file:projects/server-attachment-resources.tgz} version: 0.0.0 '@rush-temp/server-attachment@file:projects/server-attachment.tgz': - resolution: {integrity: sha512-owWsLRjmcWngpuwA21JWSyUGIRA1Kv+tCttGowe93xCr38xohSK65MVRjy1by9UVYZ+DkF4nnJBfzM+Y3oyU0A==, tarball: file:projects/server-attachment.tgz} + resolution: {integrity: sha512-Pzf9R3OtMC3NS0HqCe0IrPoMCQgDUdI8DdmJTWTF8YLecU4RxkakRor/JvKVorbGvPPuSyk7VKP2EhYxI3zb4w==, tarball: file:projects/server-attachment.tgz} version: 0.0.0 '@rush-temp/server-backup@file:projects/server-backup.tgz': - resolution: {integrity: sha512-GXg+Qicir2LQraAc7kZ+YrxWPpEoLmplB6ym5lSsYoqcD9EsWiwaUeZdxY1HGpeN5NRZUgJUZDOPzhzmBNPvCg==, tarball: file:projects/server-backup.tgz} + resolution: {integrity: sha512-KBpqEiy6Wqy62NoBF6C6N4asCfl32CefT4OmE16veNK41qXSALBJONXT0OovNMGkULuQzLKCvtq5J4bJk7XglQ==, tarball: file:projects/server-backup.tgz} version: 0.0.0 '@rush-temp/server-calendar-resources@file:projects/server-calendar-resources.tgz': - resolution: {integrity: sha512-s8Ya82BcJnhgE4qDrktthxHEHo5+THKXyDXFBd6nzZvZxIs7pIf1JZjfz/zVtKi6HDZT6U0SDipDq2XU7FyPnQ==, tarball: file:projects/server-calendar-resources.tgz} + resolution: {integrity: sha512-OpAIfFOkVEV1e5KXKEQp+w8XYW4NluTcmuL9l1GRl/FD9+GEHsbdH0FsrCadZlCB03OD0JHmwIVqOh6c+eTtTg==, tarball: file:projects/server-calendar-resources.tgz} version: 0.0.0 '@rush-temp/server-calendar@file:projects/server-calendar.tgz': - resolution: {integrity: sha512-qQr9tSWkpnlfyL5lnuROj1LMmgodqomsOMqQT9OLPirMBhG0YuZa0YgFhyXwmsSHON0T/fNdRNuZxMzQxQXkow==, tarball: file:projects/server-calendar.tgz} + resolution: {integrity: sha512-8a5fzEbUPx6UUuajGzebXs3qsrFKmhkdel07jMpzt+KCRh6S7bNO7MqElqtMeC42bf9D4lacdk56CTGeqh7NLQ==, tarball: file:projects/server-calendar.tgz} version: 0.0.0 '@rush-temp/server-card-resources@file:projects/server-card-resources.tgz': - resolution: {integrity: sha512-muhdL7qOXJwkjvHMPwla5Td/isBpwEfTR7GwQ8CTvqZ7RbQErzhGjwJRMHEk0WzbT1WqqXLzPKCmRv/bBKMuMQ==, tarball: file:projects/server-card-resources.tgz} + resolution: {integrity: sha512-bZ+KktwBq0K0qxJWSQRGaau9NSTTfgicl7PTGeovEmi1zI2ChWCBI7zzPi3nKCZH5gGhVQmsm7VaLUGtCf913Q==, tarball: file:projects/server-card-resources.tgz} version: 0.0.0 '@rush-temp/server-card@file:projects/server-card.tgz': - resolution: {integrity: sha512-Q72AFi3vK+zFEZ4Rq2xlyBYcUKvXIVbvS6hE2A9sU2jLO+gfex7Y9M2WC/ZV0Cw5qYbcIwEaXgm65Zv9S4ZIzA==, tarball: file:projects/server-card.tgz} + resolution: {integrity: sha512-chyMlht0jWR3DtvEIvpG4y7Og0zP1hOS9dXGimE9uMbgrkzDrm9MdXm/pFwAYfs/jjEIno/CCF70PixYTUwc7A==, tarball: file:projects/server-card.tgz} version: 0.0.0 '@rush-temp/server-chunter-resources@file:projects/server-chunter-resources.tgz': - resolution: {integrity: sha512-J41SpIVltba1OPNkMg1oZtffn64Qs1Wpl9Hbr1SYEZT3v1Fl+rg7RX6Ie7JqHILmfMGPd3Cm4Gcr6AvcjBdIYQ==, tarball: file:projects/server-chunter-resources.tgz} + resolution: {integrity: sha512-SgqTJ9DItZPSUBatBMree1dDaVekKXUVt97rF4vJF6P2UQfZzRHJpRk9nnLcKdZ3wReXLZXg3Mp+0XrUZkho2A==, tarball: file:projects/server-chunter-resources.tgz} version: 0.0.0 '@rush-temp/server-chunter@file:projects/server-chunter.tgz': - resolution: {integrity: sha512-DmppjK3zILtBICNXCkkaLnGqj03lQAgBERygkuNHCMectiEiVCuCDuuOC9pT0pX04zR+EQfpwVA3NQFmZvfcPA==, tarball: file:projects/server-chunter.tgz} + resolution: {integrity: sha512-W1OfA+gUg+JTFEeOptO19xk/xp4+YxmdYD9FZGqjm3SPJVNzTqCiBBPuL4h9wQGURZoxKWVHwGVtEQ4b8zfHSg==, tarball: file:projects/server-chunter.tgz} version: 0.0.0 '@rush-temp/server-collaboration-resources@file:projects/server-collaboration-resources.tgz': - resolution: {integrity: sha512-j09Er+SwoTY8PJaaIGERdCXco5dFelrHnMwSuDC0d3tFgQSlzs+NKRIpTiS5qSQj68L5rnKKyyVuiyx0V2in9w==, tarball: file:projects/server-collaboration-resources.tgz} + resolution: {integrity: sha512-LmuogEXl+NT5MiIUVnm5tRXCiRjnCy1Ip52nTPu0Bpp8rs4qG+y+nuOUea2inPFlf+PtxLrVBUBKo4Tdw9SdUw==, tarball: file:projects/server-collaboration-resources.tgz} version: 0.0.0 '@rush-temp/server-collaboration@file:projects/server-collaboration.tgz': - resolution: {integrity: sha512-cBCPjqpZ93oD1ts93qhlm7OViJUtwSUUtjh9EWegD5835UbapOUIxcS7R/xILVKUv+4XvurnwEhv7fVNYIaS6w==, tarball: file:projects/server-collaboration.tgz} + resolution: {integrity: sha512-ZFBhkTcykham03eCP6A0WNP+JFm6OWtCIxPTWKDSvEBvb2yg2aq3tZNZfw3ePmY+51VXkWFc27mzpwdoBSPUtw==, tarball: file:projects/server-collaboration.tgz} version: 0.0.0 '@rush-temp/server-contact-resources@file:projects/server-contact-resources.tgz': - resolution: {integrity: sha512-xDQAcjpdKiJlQU+Evh6j23BKcasU33gvKQMmku4e1hq30a4d+FA6QF1XuDhoj0OcB6zcKZBxmkWEzUq/U0+6tg==, tarball: file:projects/server-contact-resources.tgz} + resolution: {integrity: sha512-wAm8c55Tdo5hk/sVsBM0M+L40lXLu2M4PX8h+dmI8vgYYcA4jSJOQjAnUdkQIkYtsZEjfydmZDJiR9/g9afQNw==, tarball: file:projects/server-contact-resources.tgz} version: 0.0.0 '@rush-temp/server-contact@file:projects/server-contact.tgz': - resolution: {integrity: sha512-ycC18quu+VszDizUJjph7HUiE8W40uTnSI7dDTvixvrjNqB9DQqLaV7yrPRP+zH2Qg4AJsJFQphBd14X6paQjA==, tarball: file:projects/server-contact.tgz} + resolution: {integrity: sha512-02io5PoQWAPKRwU87znwTPHgOTuBdMc/7qO4OyjOIkNhMj87jH147mPKVpauAWzg/4kcKfUwZUOCuVPxVGPdMA==, tarball: file:projects/server-contact.tgz} version: 0.0.0 '@rush-temp/server-controlled-documents-resources@file:projects/server-controlled-documents-resources.tgz': - resolution: {integrity: sha512-J/+tfHPTmPRCP3N0Bs/oFabHfP39vYAZkZNLsOeTwMgCSC/PSqVsiS1qMxtEzZm30SOeLsQnqtQkxkNP92J86Q==, tarball: file:projects/server-controlled-documents-resources.tgz} + resolution: {integrity: sha512-l+C8vzYQs1ESmtRbaEim7qmS07CY4avWCWHMRrO+Yo1MtMe4uf7wLDWTA2mWvJ2MeUGAu6lXahet24W3wLVVBg==, tarball: file:projects/server-controlled-documents-resources.tgz} version: 0.0.0 '@rush-temp/server-controlled-documents@file:projects/server-controlled-documents.tgz': - resolution: {integrity: sha512-eW9iJo4B1818C13PED8GXjptz9WbLcBlCU+7bMukt5fkcqZRY2xT58WxfvlBuvVg4LIryavb+C7RnpxuQfOUQA==, tarball: file:projects/server-controlled-documents.tgz} + resolution: {integrity: sha512-WOzlkPxBFPvDhNSbNOcgG2BWrvi7eNyvtnhePjI3k/2Xj6y16R1/abwKpq9U3oKj28APjI/Nzf/ysQFI634HWw==, tarball: file:projects/server-controlled-documents.tgz} version: 0.0.0 '@rush-temp/server-document-resources@file:projects/server-document-resources.tgz': - resolution: {integrity: sha512-7iUvCATWdN35U3Y26dlNeKD6GTOiXX27ysB1da8JyPBrcoCiiwxAfekvJ7jrOLItlwtMLkGAcGtE6lgf4Vwluw==, tarball: file:projects/server-document-resources.tgz} + resolution: {integrity: sha512-fHJI4XfJxPfD8TVqXyMtbHXNLUbgBVFNrfUTcs+QvA3DlVHSS/xU/2EqD1oQUXKxmfC6LUcGfx9u7LmLifGu5w==, tarball: file:projects/server-document-resources.tgz} version: 0.0.0 '@rush-temp/server-document@file:projects/server-document.tgz': - resolution: {integrity: sha512-0nJnRxz5mOehmlk4JyELWZvoPz/IBQqy7kJ/GFDwUBsyTQqOlaJmxr5lVWBEkpYUlfOrrTAjsoCTC0Kkq/r57Q==, tarball: file:projects/server-document.tgz} + resolution: {integrity: sha512-RnCPHTU2RVr4k4m3UUrAs/6WSdi3rIcAJ6KcWGO2Bl2oWHXr6eAMqBwvGYumvQ8dAcf4hy4P5xv7FDZnGeQw/Q==, tarball: file:projects/server-document.tgz} version: 0.0.0 '@rush-temp/server-drive-resources@file:projects/server-drive-resources.tgz': - resolution: {integrity: sha512-54fG7W8X+jDMhxxBZblZygVKuGU7YtTn7tvQ7XCPLS2L8E/re5Hj70RK1nthakU/ms9p9ft7c1ryrvNjow4NoA==, tarball: file:projects/server-drive-resources.tgz} + resolution: {integrity: sha512-r/F32VFg+fMvkRTZQx9QpqzbmbVIczoBhtk3+0ASPFp4H7YUxNMJ/hbd5nWoxmjJatrMWH6p7zK0sP9NLCagwg==, tarball: file:projects/server-drive-resources.tgz} version: 0.0.0 '@rush-temp/server-drive@file:projects/server-drive.tgz': - resolution: {integrity: sha512-l8mH3ve64a/2E9xMEmX5AqvTcjyDyxB0ajZk5uHuezVUqVoZ6CqZzxIfe8ripkFnoEBv03gK4RcrfJ5mTL1Abg==, tarball: file:projects/server-drive.tgz} + resolution: {integrity: sha512-RxwC+k6IJR1RtLCWEoze5U5ohs06fLf0fLexIsbLK+didLKlTrVFLvp9SO8qd2FshzkPK/tKzzEXbJIV4pu/IA==, tarball: file:projects/server-drive.tgz} version: 0.0.0 '@rush-temp/server-github-model@file:projects/server-github-model.tgz': - resolution: {integrity: sha512-L0aCrnwO76DwN3M1hJ2WS6AMVW/I6inhhJcXt/92LRD/9iOB//JAM+LXNF4w5GeywWDd9gzDjDx9JDiTQ4+QEg==, tarball: file:projects/server-github-model.tgz} + resolution: {integrity: sha512-jYbFr59DwjUbRfxD1bbVVXGpS3bfk0GV60iOrzmCxVkovHj0GjcSyC2BiQarqtDe0l8mydUJ00jFBBvklsXMmg==, tarball: file:projects/server-github-model.tgz} version: 0.0.0 '@rush-temp/server-github-resources@file:projects/server-github-resources.tgz': - resolution: {integrity: sha512-00V3E8KXRybyxNLNOlvskD5fiGBbVToslyaV9Az0nUxRMbxSfQB05+MDEzVaOAf05lDlWpAvOwtes+zR4ZKXRw==, tarball: file:projects/server-github-resources.tgz} + resolution: {integrity: sha512-4T9kKTlyeXF5QT3s3vLabmTszk3G3M17TJqK3lw0c3XVriHVU0CDfWfDhXVoSo0cxu7LUbl/yBQchsB8ST5fxA==, tarball: file:projects/server-github-resources.tgz} version: 0.0.0 '@rush-temp/server-github@file:projects/server-github.tgz': - resolution: {integrity: sha512-lwqS9g0GjmjcU0PzRSw/tAsco3XRB++QQ93V/595QlRgdZlciLP9Qa8FdAjC/8TJ1OMixroXne7zC1Bkq1LTWQ==, tarball: file:projects/server-github.tgz} + resolution: {integrity: sha512-2WW1tG+XTZzhwCqg+XuYhJ/u70kUfSNHBHy5k1Yfq52UZjUGNc4o5Ubzo2VwYn3AwLP5AmUfhGH1lAxhOeE0cw==, tarball: file:projects/server-github.tgz} version: 0.0.0 '@rush-temp/server-gmail-resources@file:projects/server-gmail-resources.tgz': - resolution: {integrity: sha512-jmyJGWJA5Y5azECj9NuqP4EUi5ZPPCjtUIsZ5cOFkXaatLwj1rIoQQb/9mcMO3RxgOMF8BXHFELJyB2R8oyuug==, tarball: file:projects/server-gmail-resources.tgz} + resolution: {integrity: sha512-z4WX2GD2G18K/QRLXeLTZqfFcqbtL/Ecycc6MQWJZJyTzG9mn0rDVDFJyeFekUXRHX81YB+N0UJ1bw09bXQ9uA==, tarball: file:projects/server-gmail-resources.tgz} version: 0.0.0 '@rush-temp/server-gmail@file:projects/server-gmail.tgz': - resolution: {integrity: sha512-TE15t5YeskyYQqSnAPIvWQh84Xz7W7g1K6N1pTWXLICiZYg8VtQAiEzVgwXU0hHQwYTGRZvnZ+jG9Qx6bGa8Tw==, tarball: file:projects/server-gmail.tgz} + resolution: {integrity: sha512-/bD/uQeS/Pg/euZTp7Bde5tcPeLhEsTK2QvAb6NHEyowwqnx007PKUa8e0VZvzl/f4nEp1yw6tuMuz6RUEgANA==, tarball: file:projects/server-gmail.tgz} version: 0.0.0 '@rush-temp/server-guest-resources@file:projects/server-guest-resources.tgz': - resolution: {integrity: sha512-ECqlCVey2EZKlUSqxT6Hv3wF9k1RCk8TX0i9RN+ssZqUuEyyGeBtU4A8jHN9DEIf09MUlmjnYhTTr8jYI4klKQ==, tarball: file:projects/server-guest-resources.tgz} + resolution: {integrity: sha512-Gk/vrnbgc83xcOG4ieOtj8EHEH66jp66B66WQZ/BtxbimW9dZTQE6Yw3HdZ9C4GTF1JTWFVKk0ZotNxdIOBTZA==, tarball: file:projects/server-guest-resources.tgz} version: 0.0.0 '@rush-temp/server-guest@file:projects/server-guest.tgz': - resolution: {integrity: sha512-gfAuEifDey2goTOqHLVFVAqHQa+vcYekUUQzbbWDCJB1DK0Xco5xTOtzf9vEcsdRjqVxGdc7zEqup7wdfInBoA==, tarball: file:projects/server-guest.tgz} + resolution: {integrity: sha512-FOsAk8gk0STit549nZBSOkmz5qf1LD0/wtz4sXrRcs4I7hO1UWlcR00q+G8Ltgv1VmIcpOBer/muFHU2XU4psw==, tarball: file:projects/server-guest.tgz} version: 0.0.0 '@rush-temp/server-hr-resources@file:projects/server-hr-resources.tgz': - resolution: {integrity: sha512-s88FhUj/IgzlmpqFjZtt/zIAFSIhsSnIBPdDeIfF/jWOa/PepyYr0ePTm/7iX2FkqPmMnE4ZAnCaDlLY33h3yQ==, tarball: file:projects/server-hr-resources.tgz} + resolution: {integrity: sha512-ZdktpLSsvuSn1gAu58NkvNQILJv1VclCSensfIOJokWMaKCdMNs0luJBHByePm2eV25x51j/TioK6YtApso52Q==, tarball: file:projects/server-hr-resources.tgz} version: 0.0.0 '@rush-temp/server-hr@file:projects/server-hr.tgz': - resolution: {integrity: sha512-KURYUfq6FE3zg0Wy0xe6n7F7/apOa0w1UEFnP+Vhc5iy/rt3WNKDq1w9rLOHNvbcHdvFv6VxXKMQ6eCemg7Peg==, tarball: file:projects/server-hr.tgz} + resolution: {integrity: sha512-x7MkB6SXs72xLBhEtDyvS7MBswUHFOA/MGx28ht0S8Z8eBn7soSSJagupoWA0FW17pJIdDPOmn4KHRCw0z4zHw==, tarball: file:projects/server-hr.tgz} version: 0.0.0 '@rush-temp/server-indexer@file:projects/server-indexer.tgz': - resolution: {integrity: sha512-0yX/IuyRgQOmcjTKPnLQmSdwBxKcbtrL2FUmfcQ2/KrTqpDXaeFofuYSXrLDCYEwPuVcHoAq6aR/ifHJLtzF0Q==, tarball: file:projects/server-indexer.tgz} + resolution: {integrity: sha512-ahtPq3JWUdALcOOT/mt/x+LgCNc/g/XTMdNgqZkNiasJ9UlXG6wbm/I8CR4ysrUbI8D1TytuM6XcqCm7KXOa0Q==, tarball: file:projects/server-indexer.tgz} version: 0.0.0 '@rush-temp/server-inventory-resources@file:projects/server-inventory-resources.tgz': - resolution: {integrity: sha512-Ly5Caj/SaEf0XBPSpQEqN0WXpRG4CDCL1Yc5K/wsDTxjdwee2FcZhFhGzopI5TM7vIb1+lBqp2EAfssVMRvVHQ==, tarball: file:projects/server-inventory-resources.tgz} + resolution: {integrity: sha512-dQY6ffP/O69HhehZWLJOZKOc3q+Huf9hGGEFZkKJIJY5ZEL4fw8s4j6XBfxCPacz3S6NzCcLUTGALBAh8Jc9AA==, tarball: file:projects/server-inventory-resources.tgz} version: 0.0.0 '@rush-temp/server-inventory@file:projects/server-inventory.tgz': - resolution: {integrity: sha512-P90jn4c8QW5XV8U2CXY/9bW5dVVIgeMC58KDCem4rCBuN5CG+WMPB0PB8mlXENRGTpbyPaUnjmHcbDAcToq8ug==, tarball: file:projects/server-inventory.tgz} + resolution: {integrity: sha512-sUg/Bp9ffZrwoY11kxfP2MP6qS64144fCzYk21V8qWhZUEcj1RNkSyieENtuUmVPtQ1uu5+w2nYBRO2LEKYSFA==, tarball: file:projects/server-inventory.tgz} version: 0.0.0 '@rush-temp/server-lead-resources@file:projects/server-lead-resources.tgz': - resolution: {integrity: sha512-/hwq45SIUtoXoegbdTo+rli5iZcXrRaa2vAKtNbvjBmqj9BYDYJDWupPYbLIACTV/5bbgqi0IVUm8qZgF6zHLA==, tarball: file:projects/server-lead-resources.tgz} + resolution: {integrity: sha512-c8EcvgZCOX8nyny8v5x6RWpj3NFkA/a6ssI/Zb3usftJqCoevhS7+K/IGV593Wmaxmd8M+pK7UWF26N0pe2scw==, tarball: file:projects/server-lead-resources.tgz} version: 0.0.0 '@rush-temp/server-lead@file:projects/server-lead.tgz': - resolution: {integrity: sha512-XpLsGcRV55TUWvnjUB/RT+jyQ/nt5jjnIf1ANjUGUO2EpOWAAKuNe+iUZexZTJinzC5m274C8bWC36tRBwl7zw==, tarball: file:projects/server-lead.tgz} + resolution: {integrity: sha512-CMozRLqLwhrtb+/EKIXHGTGusD++SMGiqObYw4yjVWk4m5N0//E/BaZHY8i2TRfxFLcbQZ2jNHQxUoKPyA5oyQ==, tarball: file:projects/server-lead.tgz} version: 0.0.0 '@rush-temp/server-love-resources@file:projects/server-love-resources.tgz': - resolution: {integrity: sha512-aKGsUFzjDwP6ykCOpYkQzQjHLh99p77aKpLkWB+NIBTo38mV/Q5dlg1VL+wvZX8IqvzPEzf7r22xSJyIfxgRhQ==, tarball: file:projects/server-love-resources.tgz} + resolution: {integrity: sha512-gnSMQNXGpPTAcmAw+KcuqFJttJ17bOVbPQ88hc9G7xoEfBiKTueiRAp4C6uMq4K4TjmCng4tXbW2Ui7XZEjNyw==, tarball: file:projects/server-love-resources.tgz} version: 0.0.0 '@rush-temp/server-love@file:projects/server-love.tgz': - resolution: {integrity: sha512-iPHWgfx0DY/flWSd8FqKPgaFVWGyU2MX8/WDprKkx7zn4i5zQfk7x8uqt069cqQoLHJTDYb/g38IOdUZD8YxxQ==, tarball: file:projects/server-love.tgz} + resolution: {integrity: sha512-UNWLz77BbgxceZBPXdAb9j+0KNWcQQh3mldfJJ/oPTVxhtYBfAI6KHNMdtXXAhBYLQdlbYlPuoiTlN/8f39JPA==, tarball: file:projects/server-love.tgz} version: 0.0.0 '@rush-temp/server-notification-resources@file:projects/server-notification-resources.tgz': - resolution: {integrity: sha512-ujHWQlEhpKdV+dDvbNvaP8+5igoewBEhyyEJabz/AR4WjBV+YLTkIb2Nt2TpbPYPWp2rTrKw4K/bImkHGkZk+A==, tarball: file:projects/server-notification-resources.tgz} + resolution: {integrity: sha512-IIfXITBJ+VMf5lcMdRl9asqhGG9epqBLN7SpCemAAScA4tQBjwjGqja+Z0Lf0G4MJXqH8wZ7rINXRGAY6/PAfA==, tarball: file:projects/server-notification-resources.tgz} version: 0.0.0 '@rush-temp/server-notification@file:projects/server-notification.tgz': - resolution: {integrity: sha512-nKKvEw2T3s8vCN+ZOijlOd6L5YQxwdnnSz5rw9ExlFV77R2690grhAQqT4MpfQsnuGR0P5X5cs8E+267p3tHSw==, tarball: file:projects/server-notification.tgz} + resolution: {integrity: sha512-2qYBBCY8HEc6Z0f1NwS1rJQlPZqHrq/Lzwu503BnO5+HJxniDpHaQiInQLOIPJv4hCd0Iu2K03wij3tdd4WoZA==, tarball: file:projects/server-notification.tgz} version: 0.0.0 '@rush-temp/server-pipeline@file:projects/server-pipeline.tgz': - resolution: {integrity: sha512-7JhDu6lxJ0LKeOiWNKMO9K8PmR4xbsOTRhVfHGw5YJ/xGM8Xs3qvd/qgW/AeBat6KF/9FJaVQzjUcOnt857few==, tarball: file:projects/server-pipeline.tgz} + resolution: {integrity: sha512-oBWfjiG4bFpxJxL1CCj2NJne0ZmH5OPuig7I4hbiqrsYDCIP/q6eEph61U+jLZ9mw14JXWQThodn3OxsAPKFNQ==, tarball: file:projects/server-pipeline.tgz} version: 0.0.0 '@rush-temp/server-preference@file:projects/server-preference.tgz': - resolution: {integrity: sha512-HHcGR7U23qh5Khwj9I8mMF5p2Jxj6zedWQpw8K1AGY7I+XtGzUPncG1cpypPXeUb3jRd2nt/8xwHPXaaRvHMFg==, tarball: file:projects/server-preference.tgz} + resolution: {integrity: sha512-c7M2Z+RLaKN3OWDlcKpIEo7Mnl1gyGHiOIQlvMK3/C7JpuyZc9IeBU0MqYjGqQK2ZPtk8Sl1JwtohUe4J+dIrg==, tarball: file:projects/server-preference.tgz} version: 0.0.0 '@rush-temp/server-process-resources@file:projects/server-process-resources.tgz': - resolution: {integrity: sha512-SwOCttKmrtxQVxVukwiKv6sXPMxtnF0MEtZ+SM5xOATNrlCPT4bw6KDmFVbxmy0qgMSXrE3LvOCznz1ygbmQMQ==, tarball: file:projects/server-process-resources.tgz} + resolution: {integrity: sha512-80t8wsOXWkW8Xd56AcnnkCf1mtQnfl/4efBEJ6PU10v0TzGFqjs/0PFfdEd9dvhUPKofUY4lg412Pcsmm9n6LA==, tarball: file:projects/server-process-resources.tgz} version: 0.0.0 '@rush-temp/server-process@file:projects/server-process.tgz': - resolution: {integrity: sha512-Hu3ad1ewUR4672pnRRN5MfkgmugDbUXl+fJM0Q0kkGS4tGqt4i0I7jEoGyO2V9lM++HoEvq+3m6Y+Wa4EybHqg==, tarball: file:projects/server-process.tgz} + resolution: {integrity: sha512-a149IoNWrZoC5iS1X3e8zOSTAe9Ib+RdpizCn3HSoQGrouCQdNrQP+NRpIZtfD4z4S+flCf2c/SavbZ7WMElHw==, tarball: file:projects/server-process.tgz} version: 0.0.0 '@rush-temp/server-rating@file:projects/server-rating.tgz': - resolution: {integrity: sha512-MVaOq273O4YfOKmFmdZUwiodKMjxSAMFMkm9i6ph84J3VBdLXIEWu5T1S91rSV+ecRgOM4A5aGDkSZbPgw247g==, tarball: file:projects/server-rating.tgz} + resolution: {integrity: sha512-hD79xpPVSOofJIcHrpN4LJVKeuzNWe5GhYIAN4aFb25bKYg4evtMF82NQ+LTJ0DklSzToLxF5MZnFabylamLsA==, tarball: file:projects/server-rating.tgz} version: 0.0.0 '@rush-temp/server-recruit-resources@file:projects/server-recruit-resources.tgz': - resolution: {integrity: sha512-9yT1+vz1CiLpsmTmCEuvRC6kWgUvdzO9iuBbpXMgsiJ+gRukrgn8nzHrFyDT/4xMbuG7J0jeMq9AQXguxkl+Pg==, tarball: file:projects/server-recruit-resources.tgz} + resolution: {integrity: sha512-inXCorPX8f9IP6QrAXM+KIX+ycM/jVcAdgLToXO0oQJO1qJzxDGFHRG7G+Yt8gWvWaOrPmFcog/9YaapCGrCVw==, tarball: file:projects/server-recruit-resources.tgz} version: 0.0.0 '@rush-temp/server-recruit@file:projects/server-recruit.tgz': - resolution: {integrity: sha512-lyNnVqRZUb/4Q6iQyBQHvAKw6YsrOIH9S43NkEcrYNrUaeykQiqi/bQ4noyqZi9sn5OCq7oXb8Ffn93h3D/cjA==, tarball: file:projects/server-recruit.tgz} + resolution: {integrity: sha512-nWOp04Vi/ICWuKvkequzsEazZ9uNNLC+tq+/Y527C8nnh9dqyTCOZdfVH2q/f11L+K6Izd+j2n2vvnpk7dCXmg==, tarball: file:projects/server-recruit.tgz} version: 0.0.0 '@rush-temp/server-request-resources@file:projects/server-request-resources.tgz': - resolution: {integrity: sha512-tT1kaQDQYvIJPDfuB0ttia93oqLeZ+Fr0XmNPFfZLPx0QkB9JqJMEbrWMP+8OWcg8oMvwWlxxXQI7x5IbKTs+A==, tarball: file:projects/server-request-resources.tgz} + resolution: {integrity: sha512-5SeS7McOHmeJ53T/VUYOHYzmzCloDXH9D6pizow/Tt5b+L3DVlyY9TBJozcFfOp7lI1RIgXPFpk5d1UbOiTE2A==, tarball: file:projects/server-request-resources.tgz} version: 0.0.0 '@rush-temp/server-request@file:projects/server-request.tgz': - resolution: {integrity: sha512-x5zDrGbtUi8t0eh1UW92jtRy9zplt0hCCmGF/78NH24UobWpi/NADrfcHVxWOpK+8ADjqp2UGvpDFsDroS/vtw==, tarball: file:projects/server-request.tgz} + resolution: {integrity: sha512-Ev8uF4nNsoo6/Kf80aWBAL5X88ZV0yU71EGhYRWUiO7seb/fxAtgc7SABdFyuj/SRUX853FTK5yv7xrknXeV8A==, tarball: file:projects/server-request.tgz} version: 0.0.0 '@rush-temp/server-setting-resources@file:projects/server-setting-resources.tgz': - resolution: {integrity: sha512-0Rxk2fQb4NvPhj8sVvEGuUonr39trsknWYMDephGPDKZNyFhcYhq2JVjN8qDrhavwMSBzK62VHjsTG6IjRPgrQ==, tarball: file:projects/server-setting-resources.tgz} + resolution: {integrity: sha512-pbon9jV5Em21WqI05Axvl+h5dkJboGbE968Ctp1Nqbi05CGwGYtxTAc/Bc8V29G74m1DVgPou6LGjmBx4JfFDg==, tarball: file:projects/server-setting-resources.tgz} version: 0.0.0 '@rush-temp/server-setting@file:projects/server-setting.tgz': - resolution: {integrity: sha512-zQSPvGPPI5992IH4MInEDjTyQw01vQGITcj3+mUXxmsazDS8IQQKqOZF/uTPLCVO844E3PzZuxJFg3gkKSiovw==, tarball: file:projects/server-setting.tgz} + resolution: {integrity: sha512-k3QbDKsKyKBYpblgibc7sssQCN50B6c//MDOZjl7USIiAXhNggVKlzxtIzXjMdVIZs6KO1GtEGIoAjnPtLA/VA==, tarball: file:projects/server-setting.tgz} version: 0.0.0 '@rush-temp/server-tags-resources@file:projects/server-tags-resources.tgz': - resolution: {integrity: sha512-E+lxibpkODCwmbphY8ABF7Sg8amp0xVthgz2S1UISBe62Fs6PuviMzLBF4EoBxaHOW7pCR2iQL8mrtY/Jg9Mvg==, tarball: file:projects/server-tags-resources.tgz} + resolution: {integrity: sha512-RSwN5c3LFTtAURvu1cVv/yP8JCTO97dazF77YPW3cfQGzA6Fsma040WdCcuM1MRbh/rSDip3mF2KCGHkhMaANA==, tarball: file:projects/server-tags-resources.tgz} version: 0.0.0 '@rush-temp/server-tags@file:projects/server-tags.tgz': - resolution: {integrity: sha512-eQmi8zzQ27sUxDs+iHcLYks7b82+3c8nshbKA7ILb2GnRN6s1nlX2ARsiduAUE38kDjeWvm5hXPdqPO7LxXKtA==, tarball: file:projects/server-tags.tgz} + resolution: {integrity: sha512-Eq/dnX6b8KKdnHHuppzowSMByQFsj5dCZnmtE/7qV05LyW6bVbxqGbSlGQq1XrcTaC6CNSGlDsPOUO9tI8NcSg==, tarball: file:projects/server-tags.tgz} version: 0.0.0 '@rush-temp/server-task-resources@file:projects/server-task-resources.tgz': - resolution: {integrity: sha512-V/07mkPXlNeIBZoPeb/lOHMJUYsG2Z0CgvvrHs9GgtljDUn8f293Z2oATHHW/S1k9oEfRE5WHSR0JTi3W3muNg==, tarball: file:projects/server-task-resources.tgz} + resolution: {integrity: sha512-3sDpiY8NM8fNlqXSM2aUonQGQMMjnsrPzSf7MPNmgGCRjsu1pSn0Fjl5FerjCTa8MplTE5KqOu8clzMtLnKzSQ==, tarball: file:projects/server-task-resources.tgz} version: 0.0.0 '@rush-temp/server-task@file:projects/server-task.tgz': - resolution: {integrity: sha512-GRy0sBjLbX1VoimEtVSTqP0naG6JzRy8C0inq5hZUsRvHM5VDHUVcq3qeZ/tU2tEizLt/M2O/jneV7GnLCogZA==, tarball: file:projects/server-task.tgz} + resolution: {integrity: sha512-GJAHQbnZTNs7wjwKQLlk9qOgVzCOb6KXEC/xCEpN5dlkm+4W2ccxJ/FvL+oVkqiHjvO8gUKk2U8qcV1eghR7Wg==, tarball: file:projects/server-task.tgz} version: 0.0.0 '@rush-temp/server-telegram-resources@file:projects/server-telegram-resources.tgz': - resolution: {integrity: sha512-inByGstzXZbrqDcGE2jybd1H2zGa3lE1Ynl9gtyZPWKV9jYSVZWwNtJQUUkujZ9Gm+kCBHNurIk+IIJDYXIgpw==, tarball: file:projects/server-telegram-resources.tgz} + resolution: {integrity: sha512-aHVIAlIAmNYmx5AHWRgfQ/C8y9KQChV9C1GT+rUNTodi+6imezeckIcvYnQtf+7M+GJidDRcgmA4muQl9tpAHg==, tarball: file:projects/server-telegram-resources.tgz} version: 0.0.0 '@rush-temp/server-telegram@file:projects/server-telegram.tgz': - resolution: {integrity: sha512-HayCg/x447BO3+GCrn2RQ7EV45k3hmD2K6GZyg6PN4vAp3AqDEgRV/GKMpxt+Hkvm6Djfn3Vj8xp3FF4hH0lCQ==, tarball: file:projects/server-telegram.tgz} + resolution: {integrity: sha512-cbVBE9Fw9S8RvUQb/FV1fpMi7bY3iZ0GV54fRnv35PssrGHhp9E/S2LjfC5H3Dtpx1qasd31q2cPMi4Hq+TyFA==, tarball: file:projects/server-telegram.tgz} version: 0.0.0 '@rush-temp/server-templates@file:projects/server-templates.tgz': - resolution: {integrity: sha512-/AlDNY7mSsliHYAd6GCB8sBebjtPhfweVjwYiHW/nY7O3aPsOf6b9oyIlAC+2oYi2PKN1grgQ0Dppx9kOHSMcA==, tarball: file:projects/server-templates.tgz} + resolution: {integrity: sha512-sf75P8DsA8CQTmJEbb9J25jlz9/3kDQW8M9NXzGCLFLmU9TeNDY5kcpERdMDJuCiBinkCcsFDt6SNOE4Nosyzg==, tarball: file:projects/server-templates.tgz} version: 0.0.0 '@rush-temp/server-time-resources@file:projects/server-time-resources.tgz': - resolution: {integrity: sha512-/sTdxrWW35mNYDnKiJjwed2Dt30PjjSwKyMLl1RvEoCvGG00MtE1/U8D5DgU+xdXqkGe4kpC2Lg5O2szwIssSA==, tarball: file:projects/server-time-resources.tgz} + resolution: {integrity: sha512-IIEutJQc2q9gBxDHqdaU9aoOy4trJG5yL/3XSj0IiQZovzM1iPsmXik93hmJH8j65Jr5X7Ctipkg1OrbCr/f3w==, tarball: file:projects/server-time-resources.tgz} version: 0.0.0 '@rush-temp/server-time@file:projects/server-time.tgz': - resolution: {integrity: sha512-p+998sskRmwuTEnh9iPkxxBosyd+9oMhQsRa2gZ21Z2wPIEQVk+5zj5ygjzeNMrJBTMS+PUZ+tA8Zzuvb4XDOg==, tarball: file:projects/server-time.tgz} + resolution: {integrity: sha512-LP4JEwFZCJG12kc0obZvaj7XSOd0KJahoEeiFZvr95BVhuXrp608otcptJXuXocNahttrFn+H0fhvNX5zuOZSg==, tarball: file:projects/server-time.tgz} version: 0.0.0 '@rush-temp/server-tool@file:projects/server-tool.tgz': - resolution: {integrity: sha512-xBKZGZTIQf5Zn3OBEID6HCOTHhgsvhVqvudd0Lcup1+WaNT5RFRDANLQ9Uqnr68vAQbWmed1Khz78EEl1oDw5w==, tarball: file:projects/server-tool.tgz} + resolution: {integrity: sha512-9Rhbhrc0mGxHuXsToE1hW+UWtZrLkriN/uGVGrJSPr356Ko79pdZerq73fjMs52dBiH+CAgNzIZUVP1xZTENVA==, tarball: file:projects/server-tool.tgz} version: 0.0.0 '@rush-temp/server-tracker-resources@file:projects/server-tracker-resources.tgz': - resolution: {integrity: sha512-Ad336xfeEnRbXZXtmDclA2zWCVta64PWuDZvVsvZbirvDGJAwXAb+3Fra0kdyEJeBtyRTTOic14FrcWsPl508w==, tarball: file:projects/server-tracker-resources.tgz} + resolution: {integrity: sha512-1ttWzmF3in5bX2Ep2cB83x5oacJgooQM6lz9bUUsk1LiJhsEoMiQqt8nvpwGyuOJDQBZbxj0/L1geZOQFSJTkw==, tarball: file:projects/server-tracker-resources.tgz} version: 0.0.0 '@rush-temp/server-tracker@file:projects/server-tracker.tgz': - resolution: {integrity: sha512-J/Dt+co+WWUSiO5NgySp1zr5WlKUbSneDvwzC5aSuTox1AQ0bw/1N1XSwEyEXT7X1UgO8PFA8PMCJpAP9D1iMA==, tarball: file:projects/server-tracker.tgz} + resolution: {integrity: sha512-KhHFsFlbBhe/lrNRrQ6M+oeuUq+MbNMrdk071f98hiOnJk3KVW6Jc5jpfp8Vz6tWGKYjZWReR+UbGDYnMFbHSw==, tarball: file:projects/server-tracker.tgz} version: 0.0.0 '@rush-temp/server-training-resources@file:projects/server-training-resources.tgz': - resolution: {integrity: sha512-Wbj705P/9E85Z2/geYTliPJESXr1wV7zhLqmW04vh9LO7hrqD3EJPT2g3ylF+uu8mji/8Ec963aNcVPn8tVNYg==, tarball: file:projects/server-training-resources.tgz} + resolution: {integrity: sha512-3jF1LJa4ReljSmaNEWtOHttwFtrqIxKVHWjeiXReVNFDYwxR2S4SxAlz+VPhlXzbYeyDNCWWIahYPXtZX+Jnhw==, tarball: file:projects/server-training-resources.tgz} version: 0.0.0 '@rush-temp/server-training@file:projects/server-training.tgz': - resolution: {integrity: sha512-pLv68FjonzczgYvZRevD1VAKUV/VwdB0FRA0qr9XRKwJAh230O91nqwmYMATYbYjdWrGnBLCug6sKiDfmtbnew==, tarball: file:projects/server-training.tgz} + resolution: {integrity: sha512-/bgVSdzNQK5NS8BoyLg0K1wTQFekhebXdPB9PFTbDfwBBSfRH0Uqenjny6H22bghBYRrbe6VFdEilGS9/DSdOg==, tarball: file:projects/server-training.tgz} version: 0.0.0 '@rush-temp/server-view-resources@file:projects/server-view-resources.tgz': - resolution: {integrity: sha512-dL3KtDqAPSWpN1Flj3yFSd8hcc3K2rHZKIjTgwYbB5tN/xvZm4Y1ezyhpFffqNJg9aM8Ei8h1dZjv7EMDobWTw==, tarball: file:projects/server-view-resources.tgz} + resolution: {integrity: sha512-UTlYC7P5B10WCMe4QKtylyAGp7d26gyde+50F3/6vbBZUOTo4gpCR4emplxvaV+DJSw5wC+MNoOQhChjdcd51A==, tarball: file:projects/server-view-resources.tgz} version: 0.0.0 '@rush-temp/server-view@file:projects/server-view.tgz': - resolution: {integrity: sha512-FflPUQNQu8whOpyKhjeau1rCAdXFRzdqY6O9Ye7b4dVbs3j5CJ1jkEDyoeNfWNp6MRIR1z44HAov1rTM0DYomA==, tarball: file:projects/server-view.tgz} + resolution: {integrity: sha512-JYDyWeohZ99Wm5gwUh4dCOsGXhhFpTPEhi+NYiwMk+LwfYWl72fwudz808qWsgnIHVTgGJ5p31m6hf9w1JXDoQ==, tarball: file:projects/server-view.tgz} version: 0.0.0 '@rush-temp/setting-assets@file:projects/setting-assets.tgz': - resolution: {integrity: sha512-zXZUR/o4i95BANm6NtT8ByowjT7nCpWnL2HkGC8BkkwHF1JylmAmd3mdJA4fHb1wmWqtGs94QYLoT9m5L1VBFg==, tarball: file:projects/setting-assets.tgz} + resolution: {integrity: sha512-obTjNoPigMtdxLWBbK7Boo/oJ1t9agf0HbScjzP4lDtA1hMea/PSWAioytLA1+cYxgbQQKj7nzHfnb3Bbgl08Q==, tarball: file:projects/setting-assets.tgz} version: 0.0.0 '@rush-temp/setting-resources@file:projects/setting-resources.tgz': - resolution: {integrity: sha512-OvFC2jshGGFNYtjrQhXqZqeoD33WqwxRifc7anOwJ9kt76LIIFlAyW83uA5bFYNRGDXONQPGo3fTGdRVM7wggg==, tarball: file:projects/setting-resources.tgz} + resolution: {integrity: sha512-sgylMn5BSo89AMPz2vY67NOcks7E5TSk2fHzBDbmTpdDWLCruL6/DjgRNwur1VnQzx60Wut0V2zvg0a9xgxmcw==, tarball: file:projects/setting-resources.tgz} version: 0.0.0 '@rush-temp/setting@file:projects/setting.tgz': - resolution: {integrity: sha512-Q9b+2d/N53yK2o0XlHIjKhlB5JjoPJsMEwj0t3RaOBu4Nm2PDtVEFbJX4aVgSdtgApL5fUHy/5UKnTzfj804Tg==, tarball: file:projects/setting.tgz} + resolution: {integrity: sha512-J2Lp71X8NxFy+7sL18IXF+i0SQ8+aG7qP06rokJCWg7zwJlnQmBvANriNlFjtTlpcGUdsErlRBlVtD2AlJnnTA==, tarball: file:projects/setting.tgz} version: 0.0.0 '@rush-temp/sign@file:projects/sign.tgz': - resolution: {integrity: sha512-vnwVj8ASx6YvEz3ldeH+Ipf/F/1Kuv2XoUfII6RYOVEXvg7VuDgrz7S9NdT65LRzUmK/t/ROtwTsXQFhkCYFgQ==, tarball: file:projects/sign.tgz} + resolution: {integrity: sha512-5Jdt6aEkrFzS355NaUHK+rj0N9xjlzvGOcrpTOPwHr1UBLTqXjPnAR/lkzLm52IRaMDPhJngcP91B4Qf+oE+XA==, tarball: file:projects/sign.tgz} version: 0.0.0 '@rush-temp/support-assets@file:projects/support-assets.tgz': - resolution: {integrity: sha512-/7tdzP+8Dv1gPsPFtgBMleoQWNaHLWg2zU4qAI4qhe6MJH1HdDpz1ltQZAcy1G7R3pwtczbHQ1/j8iei1+ofAw==, tarball: file:projects/support-assets.tgz} + resolution: {integrity: sha512-qxQPFLzFkfKodL+v1FTm/YRb3PgRKbeVcM78oArFXSXZJieG0LhhMDnjntxbbcR6NISyu0Lu9jmKJdS6L9JDzQ==, tarball: file:projects/support-assets.tgz} version: 0.0.0 '@rush-temp/support-resources@file:projects/support-resources.tgz': - resolution: {integrity: sha512-Xp8YP1N+2quNXAdpFWvK4FTRvS8UudyncjDOGXZaDuFxnRMMUGEmUXYJEINCOu1v90NOfOgaTy7NTqiFilI2kA==, tarball: file:projects/support-resources.tgz} + resolution: {integrity: sha512-VfYmybeOREsJSLWjB2pmc8ievE9kAtyvS0jFst+qWokpN27ZXOtDebLkR/A2lDTkb7rSvK08/xhKs38J9LkPJw==, tarball: file:projects/support-resources.tgz} version: 0.0.0 '@rush-temp/support@file:projects/support.tgz': - resolution: {integrity: sha512-UNUtFFo/XM3UFMRB+HkGSHwG4TGdLxA6Zzjg3KagE1HVuO16ZVDbJSxZ0ACJ8gCigpopGDLEX0iohVLh+mTNwg==, tarball: file:projects/support.tgz} + resolution: {integrity: sha512-g7pDYDm8asXj/GiOszlEMMZseTVLLtCssY9OG7cdJH35/fgtDO8Udn55nD/r4E8Uz8+J/5RgdkHZXRDMGLF++A==, tarball: file:projects/support.tgz} version: 0.0.0 '@rush-temp/survey-assets@file:projects/survey-assets.tgz': - resolution: {integrity: sha512-P9XizBzPBlklhenlf7OMbuJrwnCvWQab6stg9M0QXAVqz0pvFv0ztp0CCX3MxVKDXWGNDd99tNDn987a3nG/sw==, tarball: file:projects/survey-assets.tgz} + resolution: {integrity: sha512-MhN4PgfwtMznYV5nnPR/SP7OCD0HCEz3S8xlJauwx5LiCSU70BBwQh7H40ojx/ugVHsN0ysotpnciIq42S0Kmg==, tarball: file:projects/survey-assets.tgz} version: 0.0.0 '@rush-temp/survey-resources@file:projects/survey-resources.tgz': - resolution: {integrity: sha512-y51t1Nj7R871/4AFaKvMVqmAgtVow+R8jw1tVrMJCxBG7uhGvaaVCb55c6PrIDM9rVzVzwu7ga3zVAreLHmoZQ==, tarball: file:projects/survey-resources.tgz} + resolution: {integrity: sha512-ri5Q84ie9m/v5CuyCccp1VfBSj2utDTBTkJ0n0IYQzInWk4gFwk4cFVpu/ZpAhYotIRZ4gxgy6e9MSnNTKF/vA==, tarball: file:projects/survey-resources.tgz} version: 0.0.0 '@rush-temp/survey@file:projects/survey.tgz': - resolution: {integrity: sha512-StLX3ZzSxR2kk7NQquWn9yTlGioZi/8tA+jYPa522O5Mp+j2YI6hE8pZPYLMlJ/lxjz2S/c5kiThofH+YGMA2Q==, tarball: file:projects/survey.tgz} + resolution: {integrity: sha512-HUS6Ta4sfWBaTGSHeNS0Q4EWCywd0X1AoYH9lvD4vH0is+P8G+lGWeRJMOuPnO/bU9FYHk/plxg3NPcoTFdong==, tarball: file:projects/survey.tgz} version: 0.0.0 '@rush-temp/tags-assets@file:projects/tags-assets.tgz': - resolution: {integrity: sha512-rkbXbD9henELcvxV1Xiv+mF2z/1gfRin+sz2n0O8rPBAdo/N7H8o8lfiteMYdtvQOHAsZY1TsgWOSzEgT1S60g==, tarball: file:projects/tags-assets.tgz} + resolution: {integrity: sha512-koyDR9ljr9o/wyE43aYJcI9/3cIG2Av9FyXGJudVOcoT9arvpbYeG4yWNW8E3O1DGCxeZinIgVXJ4ZkHNJlDkA==, tarball: file:projects/tags-assets.tgz} version: 0.0.0 '@rush-temp/tags-resources@file:projects/tags-resources.tgz': - resolution: {integrity: sha512-hmlD9sAvM5Itc5L/qyghEGoLiBu2sIKJuHzkhl3C/vOCv7hf5Df/AihCylR60jGHg2YBfAC7lDC1iW4mHbXFzw==, tarball: file:projects/tags-resources.tgz} + resolution: {integrity: sha512-o6pux80zx5nkNZ3xJbKJW1YzZvMJ3gi6BBlIR9Od47zvdNDY1KgttdUk+ck69VcA/aNJEqXNl7luwemdYDQKiA==, tarball: file:projects/tags-resources.tgz} version: 0.0.0 '@rush-temp/tags@file:projects/tags.tgz': - resolution: {integrity: sha512-I4HKrRUf4JN2YEtfpJ7HakXGkY19bdMEL0YdLK7Wof9ZKzu93+6qRTW1ahTQ+ri2E4fFTQ9Crxv0mqu426LdtA==, tarball: file:projects/tags.tgz} + resolution: {integrity: sha512-n6SgU2GfFxk5En2kc2aSEPUrYImDYIHrRuzFBRGm+fy+YmRQ81gMi+xDOjvbrgDUUxFB6wFw/KHQOdIUuPpqsA==, tarball: file:projects/tags.tgz} version: 0.0.0 '@rush-temp/task-assets@file:projects/task-assets.tgz': - resolution: {integrity: sha512-FLhk7pkVmAZm0hUEimJGMr9pXmL6x9fJFjf0xLgPupPwJaoTZIWLPTXBDT4v03h4fr29vmf0xKaciAHaMydCzg==, tarball: file:projects/task-assets.tgz} + resolution: {integrity: sha512-o29+MauRRLFwM1yMGJVrzRHetcB4OPmdtnRky6zKZaG+hAbek05JzIq+NOVKmLOzsgLRR0vpB69A+LeoHLVsDQ==, tarball: file:projects/task-assets.tgz} version: 0.0.0 '@rush-temp/task-resources@file:projects/task-resources.tgz': - resolution: {integrity: sha512-4uOXtDhNTtq3EgULwxJMknrqAzlLshFWjO5RX1EI11ihmO3bIRT5WLj/okjP5qOJ5M60YDZDC2EqZb8WGN23Vw==, tarball: file:projects/task-resources.tgz} + resolution: {integrity: sha512-vDfn6Qr5CgDwjX3pImeds2ECoXFWuq69Auk+3qkG/onymkj7ksKj5QKNFA0lBf6XwaA9d/X6EeVqpHbeFRwP9g==, tarball: file:projects/task-resources.tgz} version: 0.0.0 '@rush-temp/task@file:projects/task.tgz': - resolution: {integrity: sha512-R6NIMPfekztYjpO4XJJ8vdXkjiweyiGDhIZ5Q9tMx48VLCAZEEg/5Cf25dUPsNuw4uEc68LWFymbq1FgNYF8QA==, tarball: file:projects/task.tgz} + resolution: {integrity: sha512-cy994+8JaasB1yjmACZIwXchHs+ZDR6EFRlQ2XQmaNvppXG9b4qzNatgNqfkhcWCOP9u1cxQxxRM5lWAE2bOUw==, tarball: file:projects/task.tgz} version: 0.0.0 '@rush-temp/telegram-assets@file:projects/telegram-assets.tgz': - resolution: {integrity: sha512-J+Z9siDHbiHLPuPxdcwFrxeZQRyTlJx1zvy9Br2MLeI7OU9h2LnpS82RbxVyl80E8SgGl0Tz+3whBBoON2MYMA==, tarball: file:projects/telegram-assets.tgz} + resolution: {integrity: sha512-twuNAVT8qVjuADpdV5CbfT3F+0I6RXQg2l/+a6+5HqRnU7LgTGbcH2yn8jJ9aY97WP857ftcD3ARKN0r0ME7sQ==, tarball: file:projects/telegram-assets.tgz} version: 0.0.0 '@rush-temp/telegram-resources@file:projects/telegram-resources.tgz': - resolution: {integrity: sha512-HuMvhcu6c35eOMevSZJj8PPoa/fX9Xd9GAps/TyGVWHKrp6ZFDusw7EJyJxp73yhQVEG//nnbpwXe+1YjdSL4A==, tarball: file:projects/telegram-resources.tgz} + resolution: {integrity: sha512-NBiPd4lKBslKCfYjbatwXUSH/yHtKeiAnH2yrUjMnnSu/YdGd0eU64ZFYKlN1vje96DUw9tSN3jZOCUW1XLkMQ==, tarball: file:projects/telegram-resources.tgz} version: 0.0.0 '@rush-temp/telegram@file:projects/telegram.tgz': - resolution: {integrity: sha512-kht8TatHX9YYZNMG7fTfvJbRe1fT9kpLxokUhpqnquIHd57OtuG+q+rZGYuH7cKd1f/rY7900D/HzHAiqNvd5A==, tarball: file:projects/telegram.tgz} + resolution: {integrity: sha512-sVJ0+nM9J/JXhXsEXZ3Nzv+rutsz+ZOvimtK/XmJd2qIUrg/oSnN09XxSVY2vpxQXXV5s3zymb9ju2O7ijkIug==, tarball: file:projects/telegram.tgz} version: 0.0.0 '@rush-temp/templates-assets@file:projects/templates-assets.tgz': - resolution: {integrity: sha512-TJjmx67QZqfMTtvQGNhA/g8AAJ+Zx69mDf1qNReCXvHjln6WqBahQLQPHcjF+McCai7UMR2ok2hvA6VjdyPZYw==, tarball: file:projects/templates-assets.tgz} + resolution: {integrity: sha512-bf25V3TrQswT5XVOjNj0CoUGikLd1W6n4SpFENiP2fQfz1YuOcvQhTJC36BwRhWH5zGNmxwVvuRNGxxMpHc9gw==, tarball: file:projects/templates-assets.tgz} version: 0.0.0 '@rush-temp/templates-resources@file:projects/templates-resources.tgz': - resolution: {integrity: sha512-zi9E8FcmZiPdX+/+xjOjkPVIAlTBZL0FypmjvBIiOyBckUduWfYUR1dtnKD5j5TGI5ut6eb4esAC26lsT2Lq4A==, tarball: file:projects/templates-resources.tgz} + resolution: {integrity: sha512-EJOCM8MBUP+ZtbwJsXx7ZQ4a4Pq2km1ZVbAuQk8mZudyZeeAHPJdBo24rhy7Gq+W3czqXFnRlcbKsopGyAfuKQ==, tarball: file:projects/templates-resources.tgz} version: 0.0.0 '@rush-temp/templates@file:projects/templates.tgz': - resolution: {integrity: sha512-LR9rVWwR6/mVs/AIUYh08xkGtLGciBebqLQEUI1jl4vQQ9mDYxQuXb6fYaO7v+xrb9U3lp1yGrkplWyNoG6Wzg==, tarball: file:projects/templates.tgz} + resolution: {integrity: sha512-xaiq/DlCuqqEcOoJxHgRTqKwTQKc4eiiUoTIIN2B369lfF8o9i3EBvswtpB3VJuw96brbDAUbXNSzurtDYijGw==, tarball: file:projects/templates.tgz} version: 0.0.0 '@rush-temp/test-management-assets@file:projects/test-management-assets.tgz': - resolution: {integrity: sha512-1tnHUjeDCbxinTB20sQ6x03NvFNa3JUVGV6YjGfUHsLNL0VBAJLxhY91IARg/ryZTUgZDO4dc0UalKQgw06iWQ==, tarball: file:projects/test-management-assets.tgz} + resolution: {integrity: sha512-bI5x8yvio3+AYQ8xxyLjRnJnUlEvMDJPx/N0kaT59JHELeeAQLDonJL0PNdZzVfBKT+GVWD/I9y6Q3JBn9+0uQ==, tarball: file:projects/test-management-assets.tgz} version: 0.0.0 '@rush-temp/test-management-resources@file:projects/test-management-resources.tgz': - resolution: {integrity: sha512-9xnAvWb0dXEaV+6ujQeCMM3nJG5W/A+DY7FwLU+risJg/JeU/SUJJ/jL+YyeKOEBQacvOuRSNKU8PzZw3L7fng==, tarball: file:projects/test-management-resources.tgz} + resolution: {integrity: sha512-wz9tFyT2jS3D7kAjIB8xaKR8PYBUIniMuuKPqqBTFu6gXyu8zvRHWOo/S0CS23FwLrfpNVh3sdYclFxUo7rXIg==, tarball: file:projects/test-management-resources.tgz} version: 0.0.0 '@rush-temp/test-management@file:projects/test-management.tgz': - resolution: {integrity: sha512-2INmc2Ct76BUZT8hwrOsPAC1Z+u6Lasa8pCStk11KPfyn63Ca0Rz2tDCPYeBdo6Qxmwb7PR+SyG5ilU3GOw67Q==, tarball: file:projects/test-management.tgz} + resolution: {integrity: sha512-U3nR0QUT0g5mUtisfyASTRp/8Kn2LjxuirjQakNTCYjLsFv6K1M1O5H3kesqsdQaLif1RIaooxkInd03huG0cw==, tarball: file:projects/test-management.tgz} version: 0.0.0 '@rush-temp/tests-sanity@file:projects/tests-sanity.tgz': - resolution: {integrity: sha512-f74RMq5aQDfuYJ7yvwcstxlY7GutiqWFWGuPBGpIIMRgg6PFevxyOpXyLqpYJUp091Dih4Vz7NUJB8R5pBz2GQ==, tarball: file:projects/tests-sanity.tgz} + resolution: {integrity: sha512-Lzpd5WYNcyz7upsl2llqmx6mAHQoAKcAbNX/p15VvCybQJ59KYjue4tjlDKBz+Sow1YJX2UNIp3fG0wxI4aB3A==, tarball: file:projects/tests-sanity.tgz} version: 0.0.0 '@rush-temp/tests-ws-sanity@file:projects/tests-ws-sanity.tgz': - resolution: {integrity: sha512-iYp1sVqBjnmydEZwK3PREeR7hJGiWLZ8/FRlRdJJNaXVJEviTe45g2zI1WtcsyRDqs/c7dHfybqsHhWS4ZSr3w==, tarball: file:projects/tests-ws-sanity.tgz} + resolution: {integrity: sha512-S+nxtSHHP/cwLF/RVsou/94YXuDSda0q0L9lhlCxOYqudA7o+OtdsZtDHaNNXeFjQvTE5w2i6IEZtHnNxuDrnA==, tarball: file:projects/tests-ws-sanity.tgz} version: 0.0.0 '@rush-temp/text-editor-assets@file:projects/text-editor-assets.tgz': - resolution: {integrity: sha512-G5/SldNI1Gulh6/Mek1P3ByP88EueTJlnjnxsIRzG8BxD2cwyzdujjjmHbWvFCsEnLtPYqAb51qMFnQRebxgeQ==, tarball: file:projects/text-editor-assets.tgz} + resolution: {integrity: sha512-LuVZUUfgwpvJ8/jXDys040awteQGy8YjXsgEBrNqp7vj7K1hqbG7RNygVXzSlAB99Zq353kpbkUREAad764kWg==, tarball: file:projects/text-editor-assets.tgz} version: 0.0.0 '@rush-temp/text-editor-resources@file:projects/text-editor-resources.tgz': - resolution: {integrity: sha512-g3HiMWrDQheAbaG47n7ZDUWz/uwXJhLfKEPZzZoQ5YIUCsuJDG5xjveFs2ovMUmuDw3gcTl38Z1Kbi9LAYRIrA==, tarball: file:projects/text-editor-resources.tgz} + resolution: {integrity: sha512-XG7+W042hTju9w1Ulu5M0bemjMed8FIgwN5k5Uxr5ZgJh2MY4xyKuIp+CIDOYH5Nn7aI8P0zjz47aSB2SOhAhg==, tarball: file:projects/text-editor-resources.tgz} version: 0.0.0 '@rush-temp/text-editor@file:projects/text-editor.tgz': - resolution: {integrity: sha512-lX/2hXeWi4K72L2FPBacc8DgRgUw6PBYF1So/gtmErv0jRodyMlIt1QdOuTtMda0g2ebCCPMahQ/tt45M0aERQ==, tarball: file:projects/text-editor.tgz} + resolution: {integrity: sha512-A1Odoxy6o5yGIW9qhM2zkFlDBy0zDcdFP3hLYy51cQ5rWwZuyznR6JAnyZc64JS3tTebTs/a4O++nK5ZZEmViw==, tarball: file:projects/text-editor.tgz} version: 0.0.0 '@rush-temp/theme@file:projects/theme.tgz': - resolution: {integrity: sha512-Th7qm8oC9szHoqvPYXiPpb1AneZsehq6UeEb0Y5se4Y44nOyPjTIyZ75492+hGsOJh2lRHdZAzys0EdFGWqvMw==, tarball: file:projects/theme.tgz} + resolution: {integrity: sha512-2HQ/eDwnfLvxkL6/3Jc5A6EHp29cm46XaoCp2voSiDSqBLTEfmXyHmHfpbeasEFcsc/JzXj7VTojFfrUMy2p9A==, tarball: file:projects/theme.tgz} version: 0.0.0 '@rush-temp/time-assets@file:projects/time-assets.tgz': - resolution: {integrity: sha512-Dr5iyGWRpyx7ZckDP2Z8gcCxlCCAOxyoQMHr1kmDIEfuHB4SqVLdj1az1EmcToBaMG2r0hDdD6ZAak6LMaJDjw==, tarball: file:projects/time-assets.tgz} + resolution: {integrity: sha512-tm4Tq2halELvriuYA69HwouuMGcoVRnhj7m9C2W9m625NY/QTwSt/zCqNNd+K85THW5xd1SAcAnAc9wFjf2U+A==, tarball: file:projects/time-assets.tgz} version: 0.0.0 '@rush-temp/time-resources@file:projects/time-resources.tgz': - resolution: {integrity: sha512-cc4I8S7my+m5ahtmxKOJ/VMZxp9kAe1E/i283rh+5lbdar9MDTYP2Cd0kTVewhYfosBSCDzHYBzwuGhvyakYdg==, tarball: file:projects/time-resources.tgz} + resolution: {integrity: sha512-fg7vxxqLdSRD+Yy4da1oXOcYacF6qSz0hxeC5eRuI48qyp963x4vOLUGiWXNbooip7xnZNXesVY9ujlRPmYZqQ==, tarball: file:projects/time-resources.tgz} version: 0.0.0 '@rush-temp/time@file:projects/time.tgz': - resolution: {integrity: sha512-2rA63SCiD2RBPIvw2FSPFnbh6yjVZuXkppP2rcTqf4zaUMaSt2yUcPNjsX8wyGY0DmSsVtSe2hkFPZ8BrJFTtw==, tarball: file:projects/time.tgz} + resolution: {integrity: sha512-sHp9TNJOQjUGYhqmf5rRFZtqRNT39zWO8A86AKEaNUElqZwgGdkC823fHdYD7yeRxxGQzDfCvnIIWJDxLVCRgQ==, tarball: file:projects/time.tgz} version: 0.0.0 '@rush-temp/tool@file:projects/tool.tgz': - resolution: {integrity: sha512-bN2C3gkXMazB5Zv3+y0I9QgOEIQ/k50dZI77iEPiWi7rLJ1jQObrL8G906Cf8728IzYw54q3y9JgxtHkDvUGww==, tarball: file:projects/tool.tgz} + resolution: {integrity: sha512-tvRjYD+Y0+lDOH9Dz5d0SJ90Hqd2k5iDJ9pgMnAkKyu9zGqemdFGc59+r/puBRMRSh3Oi/O5J9Jiu8sAkSvxUQ==, tarball: file:projects/tool.tgz} version: 0.0.0 '@rush-temp/tracker-assets@file:projects/tracker-assets.tgz': - resolution: {integrity: sha512-gKBl2i3Q9ZAxZCMyxtl0wKON25j3jRLK86Q/leINyWQx2lot8vM7QzO3mok+JdasrhbE0QAfxIpA35iOtapdyQ==, tarball: file:projects/tracker-assets.tgz} + resolution: {integrity: sha512-gOVFFkykhekwHJhtFJXrtRqu5738fALdo5I09lYuDc5E0hMXLGo+TqDuF4Q7SWuHgl0IGps6Q+oHP+wG3oEWVQ==, tarball: file:projects/tracker-assets.tgz} version: 0.0.0 '@rush-temp/tracker-resources@file:projects/tracker-resources.tgz': - resolution: {integrity: sha512-0WK8RirOqLOWE6jtytmon7MaE8nv0AKMykl6M5f+KrfECWr7kl2X8kWfno73vAgC5bXVqP3bJqyrM0jBdTthlQ==, tarball: file:projects/tracker-resources.tgz} + resolution: {integrity: sha512-SDW+nVVXtsWBS9+pzaYVstODW+NglQF0QsYmtnNDXDQaQ4guaRPi7N/G7f3eKj2vL0SSMpa16R2dnknabMqTzQ==, tarball: file:projects/tracker-resources.tgz} version: 0.0.0 '@rush-temp/tracker@file:projects/tracker.tgz': - resolution: {integrity: sha512-LiMD+U76gp7sixUZhqqGTmBx5q5lkDIJ/gOitiRPeaCVJvpb6mMpqNETW66DajGW2A8qZnaxjs2B8U/5KXB25w==, tarball: file:projects/tracker.tgz} + resolution: {integrity: sha512-Hkp3k4KTQT0louWoZMNMicn3BM3EMo8R4JGnKf4gVhC797mhEJbocrqflLmjURSaN8dz5E9ZcwBm3HDZHDHqAw==, tarball: file:projects/tracker.tgz} version: 0.0.0 '@rush-temp/training-assets@file:projects/training-assets.tgz': - resolution: {integrity: sha512-11Z5iHikVVPumBj6MxEI9VXZuZxitoGsawwlYtU5Ms/fdk/4bsJ/n2euk1W46hsOGlCTMfsRCP2Ozw+6TjOzCg==, tarball: file:projects/training-assets.tgz} + resolution: {integrity: sha512-mdFPtDJmEs8WjrJns59M8NKSx7N3257apkToHlzmx9WYjRlSANO/3vr7RMLnURJSdW/iFlBOIqtJYHZpAIIYmA==, tarball: file:projects/training-assets.tgz} version: 0.0.0 '@rush-temp/training-resources@file:projects/training-resources.tgz': - resolution: {integrity: sha512-JMWEWk1jYNNTYw4YDIiP73qxhUy3MwuYCo3pBlfISkyYah5Fsxzo8KR7u7taCe4mzIpx5TVdO5yDMsaYkTfgAA==, tarball: file:projects/training-resources.tgz} + resolution: {integrity: sha512-u+tnMLSdTkVEU4S4572K4N8dvyJUzIY9iNQSDGhGyKiyzqJ9slBzmGJPSo7NTcNC5YBSIKDgvBhGMf/MybiS6Q==, tarball: file:projects/training-resources.tgz} version: 0.0.0 '@rush-temp/training@file:projects/training.tgz': - resolution: {integrity: sha512-1MioqvZ/RwFY/0jD9XkfTwqGeSg3FiVxeMYSKJ9t/yk5tiNjDF6fTQBS7IkOMt/E7Inw5JuRdQAsc4It0ocRUw==, tarball: file:projects/training.tgz} + resolution: {integrity: sha512-ilRlVFlifo8H3vfYseN3wjIuDpOnaVGWv3Qiex1pOB4XeI20OQCL1hylcEX1oRI+AKX4ajBqtfLUt8HodCvgYA==, tarball: file:projects/training.tgz} version: 0.0.0 '@rush-temp/ui@file:projects/ui.tgz': - resolution: {integrity: sha512-+6AzajBbSzzszcLNwMl7ufcvbnstvoRLggNPNyOHSGERKYe6pKypFTlp4bzR66puuD8175Yr+NELtbCAkbew5Q==, tarball: file:projects/ui.tgz} + resolution: {integrity: sha512-9+1K33YJyEZ65QjSoTdg/RY0VLx9hrrUL1KMkNpI/cuoKH4+79gSuX9qiUnE9d9QGwX2T91WM3iU+/mBVvRXfg==, tarball: file:projects/ui.tgz} version: 0.0.0 '@rush-temp/uploader-assets@file:projects/uploader-assets.tgz': - resolution: {integrity: sha512-FLTF+kGdsoHkMl8c6FHcZ7fQp3tweYA7lLniWC+Gk7N9FlWRyb4V7u7AhG/t3Hx9j+RItt4SHn2tJfrm1wVVRA==, tarball: file:projects/uploader-assets.tgz} + resolution: {integrity: sha512-rr+2FEWpl9edIZOWaoVCA9u+1jwS6vLX4gVv/2btIWXx/JvmmxjaatahZGML/xdkHRxRwkhA7f/+tJLHKWcWqw==, tarball: file:projects/uploader-assets.tgz} version: 0.0.0 '@rush-temp/uploader-resources@file:projects/uploader-resources.tgz': - resolution: {integrity: sha512-i9WT+ffz2yzyhIgpa93OjB7QjgXVGdJliQ4ITK+N47RG1VBhdGxPm7GVoeqLwOuxpB/Jmm7Zyzdr/UEiZ3bDKg==, tarball: file:projects/uploader-resources.tgz} + resolution: {integrity: sha512-ePLhSewTgXaRj/jN4US7NCnW3HVw57qWY52PPaAcYDA2z95kFTVQjhF9YnwvqF7X7alEDAMp/KFni4qEDAkp4w==, tarball: file:projects/uploader-resources.tgz} version: 0.0.0 '@rush-temp/uploader@file:projects/uploader.tgz': - resolution: {integrity: sha512-ESXAb8fD+j1Kc8sRNKlAhLGLmcdNDB4LHJpWTw1PkOGsbESGLiGz9Yv/jay6J8Jb5ERW4NZHelqbGkIF7s0VRA==, tarball: file:projects/uploader.tgz} + resolution: {integrity: sha512-ptEmm5YY62GQKqFe9hUCHfio1S8kmQ+LwB7FFw8ofG//iu8ncFLQeeOHRA6n74VxGPgNDoC7ThaovxeeFVXxTg==, tarball: file:projects/uploader.tgz} version: 0.0.0 '@rush-temp/view-assets@file:projects/view-assets.tgz': - resolution: {integrity: sha512-T0z1tcp3qpYYrQ1lkYYypCeTk8BM9VSeRKvX1Wg3G9yyIXPA2/gJ5Y1RVOqKaoK/AGJ+Q/U8GmfYxKHhv/i3Xw==, tarball: file:projects/view-assets.tgz} + resolution: {integrity: sha512-PeK2go8BEh07nFNWqpasuA85p98wECtUOlEeZ+ZIqMh/lff+4AhfiQr98LQKAsQDHIUHlaNGeASmRjkEzCED7w==, tarball: file:projects/view-assets.tgz} version: 0.0.0 '@rush-temp/view-resources@file:projects/view-resources.tgz': - resolution: {integrity: sha512-ZSzk9UXkaaIIiVuIqRsAsGzrktsX5vct/fAWpAhvi4MY3oMrCOYeatMlx7UCHt5PpGudReotNErapKdrTjm4ig==, tarball: file:projects/view-resources.tgz} + resolution: {integrity: sha512-C2tQ/nx/JgwFyXOnYMt9ezrobYidu+qjLE61Klg6gD04R//6vZ04UJfAj/Bc/ruCJAX4UNpVKBH08QSL+SBWrw==, tarball: file:projects/view-resources.tgz} version: 0.0.0 '@rush-temp/view@file:projects/view.tgz': - resolution: {integrity: sha512-4QdpIpszT+6BZVrUo4UwebqUsadJWlWq3epikTj03Q16qTNUotE4F/JernKlafsbNKMH7AI7opfQvAHSUO5vVw==, tarball: file:projects/view.tgz} + resolution: {integrity: sha512-WGW60RB3OPMxlQUXiqLYKqU01IoWoOZheVWVS2wIIoNeVvuHaLlmEU8oXiZqW4vEVkCLJMHiUAzgf7Ry6EaYrg==, tarball: file:projects/view.tgz} version: 0.0.0 '@rush-temp/workbench-assets@file:projects/workbench-assets.tgz': - resolution: {integrity: sha512-KwdVlto5koUbFwfo96IK5qIleddq1GJqrZWsoQxj9f/ncjO2BxeNiIGuSusCG5jSCXJqjdKKYWc5yW10NAJUsg==, tarball: file:projects/workbench-assets.tgz} + resolution: {integrity: sha512-xz/RGGWsrHWVT9ffJGSUV7bp70vHwJzW/TUYcjBSllG0I6JKb14nYfeEqv0aQK2PhP2eykW9wpT925J+pK6SiA==, tarball: file:projects/workbench-assets.tgz} version: 0.0.0 '@rush-temp/workbench-resources@file:projects/workbench-resources.tgz': - resolution: {integrity: sha512-70zo8AymqNIrB7NT2SEyitNPHLqGqYsNPkzwb+7EAZXX6G/4CIwRxkqOLM1F2XHUfF9hxMWi0qvoQ206lBsMCQ==, tarball: file:projects/workbench-resources.tgz} + resolution: {integrity: sha512-szexst6x7+V02B7JpO3btyms98hxGKymVkymoOkYERHIn6xZfh0W8uyZatdNP9wR6cX17AalfbeWMQ/IOb3d+w==, tarball: file:projects/workbench-resources.tgz} version: 0.0.0 '@rush-temp/workbench@file:projects/workbench.tgz': - resolution: {integrity: sha512-uWn96K16XSMIngimjWfgYCcr/wb/QLOtwkVva01MXcz7kaS30cZ6GG1Gd9ewXxuFKKXzbXMeVZu8NAyHaxKToA==, tarball: file:projects/workbench.tgz} + resolution: {integrity: sha512-DhLML3QsP7Kz+Q06kxFbTSNItFte5Hw5B+jKI9AU+UWrS1qUWScS1dnBMoxx73IiNTi8WFbISPMoLDfZv+Y84A==, tarball: file:projects/workbench.tgz} version: 0.0.0 '@rush-temp/workspace-service@file:projects/workspace-service.tgz': - resolution: {integrity: sha512-eVQictguitUM+PVpOg4lSLfzYHwhVbrLA/Dwn7UMu74bWjbbsAVMkJ8WuJCRFZWbSUsaceK1BGnbwDp9krPX6Q==, tarball: file:projects/workspace-service.tgz} + resolution: {integrity: sha512-Up4cIVGFCrvdSb9Eay6eP4VIIt6HxNGd3Kd4QO9Xt/2Cll8bNh9+axs3m7JXoJudHiq1B9WvwBA2TbPx8PrkDQ==, tarball: file:projects/workspace-service.tgz} version: 0.0.0 '@selderee/plugin-htmlparser2@0.11.0': @@ -6584,6 +6610,9 @@ packages: '@so-ric/colorspace@1.1.6': resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==} + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + '@swc/core-darwin-arm64@1.13.5': resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} engines: {node: '>=10'} @@ -7156,6 +7185,10 @@ packages: '@types/express-form-data@2.0.5': resolution: {integrity: sha512-zn1Cvy/scDOgV4j5pUKxsPQQVIX5vAGL6xjLWteSApd3HAmmfbdeZ3WMbFAks0kIaAxbSIT79W9GB4AVkS6fOA==} + '@types/express-rate-limit@6.0.2': + resolution: {integrity: sha512-e1xZLOOlxCDvplAGq7rDcXtbdBu2CWRsMjaIu1LVqGxWtKvwr884YE5mPs3IvHeG/OMDhf24oTaqG5T1bV3rBQ==} + deprecated: This is a stub types definition. express-rate-limit provides its own type definitions, so you do not need this installed. + '@types/express-serve-static-core@4.19.7': resolution: {integrity: sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==} @@ -9677,6 +9710,12 @@ packages: resolution: {integrity: sha512-Efm2t+xL6vEqUPs4kAyBY9xG/8LGpbSVnnFLZw6HgdxHzSRlg2/sNlA2UdMlcIKMH+LDOn7NKXUDGJK125F4rw==} engines: {node: '>=10.7.0'} + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express-static-gzip@2.2.0: resolution: {integrity: sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==} @@ -9736,6 +9775,9 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + fast-text-encoding@1.0.6: resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} @@ -13206,6 +13248,9 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + stat-mode@1.0.0: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} @@ -17757,6 +17802,11 @@ snapshots: dependencies: playwright: 1.56.1 + '@polar-sh/sdk@0.37.0': + dependencies: + standardwebhooks: 1.0.0 + zod: 3.25.76 + '@polka/url@1.0.0-next.29': {} '@popperjs/core@2.11.8': {} @@ -18983,6 +19033,8 @@ snapshots: '@rush-temp/billing-resources@file:projects/billing-resources.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.11)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3))': dependencies: + '@hcengineering/account-client': 0.7.18 + '@hcengineering/core': 0.7.19 '@hcengineering/platform': 0.7.17 '@hcengineering/platform-rig': 0.7.19(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) '@types/jest': 29.5.14 @@ -23865,6 +23917,7 @@ snapshots: dependencies: '@hcengineering/core': 0.7.19 '@hcengineering/model': 0.7.17 + '@hcengineering/platform': 0.7.17 '@hcengineering/platform-rig': 0.7.19(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) '@types/jest': 29.5.14 '@types/node': 22.18.13 @@ -27667,6 +27720,45 @@ snapshots: - supports-color - ts-node + '@rush-temp/payment-client@file:projects/payment-client.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3))': + dependencies: + '@hcengineering/core': 0.7.19 + '@hcengineering/platform': 0.7.17 + '@hcengineering/platform-rig': 0.7.19(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) + '@types/jest': 29.5.14 + '@types/node': 22.18.13 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + cross-env: 7.0.3 + esbuild: 0.25.11 + eslint: 8.57.1 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: 2.32.0(eslint@8.57.1) + eslint-plugin-n: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: 6.6.0(eslint@8.57.1) + jest: 29.7.0(@types/node@22.18.13)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) + prettier: 3.6.2 + ts-jest: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.11)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.18.13)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - '@babel/core' + - '@jest/transform' + - '@jest/types' + - babel-jest + - babel-plugin-macros + - coffeescript + - jest-util + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + - supports-color + - ts-node + '@rush-temp/pod-account@file:projects/pod-account.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(encoding@0.1.13)(gcp-metadata@5.3.0(encoding@0.1.13))(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(snappy@7.3.3)(socks@2.8.7)': dependencies: '@hcengineering/analytics': 0.7.17 @@ -28974,6 +29066,73 @@ snapshots: - sugarss - supports-color + '@rush-temp/pod-payment@file:projects/pod-payment.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(bufferutil@4.0.9)(encoding@0.1.13)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(utf-8-validate@6.0.5)': + dependencies: + '@hcengineering/account-client': 0.7.18 + '@hcengineering/analytics': 0.7.17 + '@hcengineering/analytics-service': 0.7.17(encoding@0.1.13) + '@hcengineering/core': 0.7.19 + '@hcengineering/platform': 0.7.17 + '@hcengineering/platform-rig': 0.7.19(@babel/core@7.28.5)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) + '@hcengineering/server-client': 0.7.15(bufferutil@4.0.9)(utf-8-validate@6.0.5) + '@hcengineering/server-core': 0.7.16 + '@hcengineering/server-token': 0.7.17 + '@polar-sh/sdk': 0.37.0 + '@tsconfig/node16': 1.0.4 + '@types/cors': 2.8.19 + '@types/express': 4.17.25 + '@types/express-rate-limit': 6.0.2(express@4.21.2) + '@types/jest': 29.5.14 + '@types/morgan': 1.9.10 + '@types/node': 22.18.13 + '@types/on-headers': 1.0.4 + '@types/uuid': 8.3.4 + '@types/ws': 8.18.1 + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 6.21.0(eslint@8.57.1)(typescript@5.9.3) + cors: 2.8.5 + dotenv: 16.0.3 + esbuild: 0.25.11 + eslint: 8.57.1 + eslint-config-standard-with-typescript: 40.0.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3))(eslint-plugin-import@2.32.0(eslint@8.57.1))(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.6.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3) + eslint-plugin-import: 2.32.0(eslint@8.57.1) + eslint-plugin-n: 15.7.0(eslint@8.57.1) + eslint-plugin-node: 11.1.0(eslint@8.57.1) + eslint-plugin-promise: 6.6.0(eslint@8.57.1) + express: 4.21.2 + express-rate-limit: 7.5.1(express@4.21.2) + jest: 29.7.0(@types/node@22.18.13)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)) + morgan: 1.10.1 + on-headers: 1.1.0 + postgres: 3.4.7 + prettier: 3.6.2 + ts-jest: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.11)(jest-util@30.2.0)(jest@29.7.0(@types/node@22.18.13)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3) + typescript: 5.9.3 + uuid: 8.3.2 + transitivePeerDependencies: + - '@babel/core' + - '@jest/transform' + - '@jest/types' + - '@swc/core' + - '@swc/wasm' + - babel-jest + - babel-plugin-macros + - bufferutil + - coffeescript + - encoding + - jest-util + - less + - node-notifier + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + - supports-color + - utf-8-validate + '@rush-temp/pod-preview@file:projects/pod-preview.tgz(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@30.2.0)(@swc/core@1.13.5)(babel-jest@29.7.0(@babel/core@7.28.5))(bufferutil@4.0.9)(encoding@0.1.13)(jest-util@30.2.0)(postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.13.5)(@types/node@22.18.13)(typescript@5.9.3)))(postcss@8.5.6)(sass@1.93.2)(utf-8-validate@6.0.5)': dependencies: '@hcengineering/analytics': 0.7.17 @@ -36861,6 +37020,8 @@ snapshots: color: 5.0.2 text-hex: 1.0.0 + '@stablelib/base64@1.0.1': {} + '@swc/core-darwin-arm64@1.13.5': optional: true @@ -37498,6 +37659,12 @@ snapshots: '@types/connect': 3.4.38 '@types/multiparty': 4.2.1 + '@types/express-rate-limit@6.0.2(express@4.21.2)': + dependencies: + express-rate-limit: 7.5.1(express@4.21.2) + transitivePeerDependencies: + - express + '@types/express-serve-static-core@4.19.7': dependencies: '@types/node': 22.18.13 @@ -40473,6 +40640,10 @@ snapshots: fs-extra: 9.1.0 signal-exit: 3.0.7 + express-rate-limit@7.5.1(express@4.21.2): + dependencies: + express: 4.21.2 + express-static-gzip@2.2.0: dependencies: parseurl: 1.3.3 @@ -40564,6 +40735,8 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-sha256@1.3.0: {} + fast-text-encoding@1.0.6: {} fast-uri@3.1.0: {} @@ -44591,6 +44764,11 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + stat-mode@1.0.0: {} statuses@1.5.0: {} diff --git a/common/scripts/docker.sh b/common/scripts/docker.sh index 345e3a54c0..83f141f8b1 100755 --- a/common/scripts/docker.sh +++ b/common/scripts/docker.sh @@ -25,5 +25,6 @@ rush docker:build -p 20 \ --to @hcengineering/pod-backup \ --to @hcengineering/backup-api-pod \ --to @hcengineering/pod-billing \ ---to @hcengineering/pod-process \ ---to @hcengineering/pod-rating +--to @hcengineering/pod-process \ +--to @hcengineering/pod-rating \ +--to @hcengineering/pod-payment diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index a240ef7c3e..206267761e 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -358,6 +358,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise l.trim()) : ['en', 'ru', 'es', 'pt', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'tr'] diff --git a/desktop/src/ui/types.ts b/desktop/src/ui/types.ts index ab9d0340df..aac962d8ae 100644 --- a/desktop/src/ui/types.ts +++ b/desktop/src/ui/types.ts @@ -64,6 +64,7 @@ export interface Config { MAIL_URL?: string COMMUNICATION_API_ENABLED?: string BILLING_URL?: string + PAYMENT_URL?: string PULSE_URL?: string PASSWORD_STRICTNESS?: 'very_strict' | 'strict' | 'normal' | 'none' EXCLUDED_APPLICATIONS_FOR_ANONYMOUS?: string diff --git a/dev/docker-compose.min.yaml b/dev/docker-compose.min.yaml index 955d91bb67..43dc5d7055 100644 --- a/dev/docker-compose.min.yaml +++ b/dev/docker-compose.min.yaml @@ -123,6 +123,22 @@ services: - PORT=4900 - SERVER_SECRET=secret restart: unless-stopped + payment: + image: hardcoreeng/payment + extra_hosts: + - 'huly.local:host-gateway' + ports: + - 3040:3040 + environment: + - SECRET=secret + - PORT=3040 + - ACCOUNTS_URL=http://huly.local:3000 + - FRONT_URL=http://huly.local:8087 + - USE_SANDBOX=true + - POLAR_ACCESS_TOKEN=${POLAR_ACCESS_TOKEN} + - POLAR_WEBHOOK_SECRET=${POLAR_WEBHOOK_SECRET} + - POLAR_SUBSCRIPTION_PLANS=${POLAR_SUBSCRIPTION_PLANS} + restart: unless-stopped workspace_cockroach: image: hardcoreeng/workspace extra_hosts: @@ -203,6 +219,7 @@ services: - DESKTOP_UPDATES_CHANNELS=dev;tracex:dev-tracex - BRANDING_URL=http://huly.local:8087/branding.json - STREAM_URL=http://huly.local:1080/recording + - PAYMENT_URL=http://huly.local:3040 # - DISABLE_SIGNUP=true restart: unless-stopped transactor_cockroach: diff --git a/dev/docker-compose.yaml b/dev/docker-compose.yaml index a5b96d30f4..20b4349358 100644 --- a/dev/docker-compose.yaml +++ b/dev/docker-compose.yaml @@ -199,6 +199,23 @@ services: - SERVER_SECRET=secret - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces restart: unless-stopped + payment: + image: hardcoreeng/payment + extra_hosts: + - 'huly.local:host-gateway' + ports: + - 3040:3040 + environment: + - SECRET=secret + - PORT=3040 + - ACCOUNTS_URL=http://huly.local:3000 + - FRONT_URL=http://huly.local:8087 + - USE_SANDBOX=true + - POLAR_ACCESS_TOKEN=${POLAR_ACCESS_TOKEN} + - POLAR_WEBHOOK_SECRET=${POLAR_WEBHOOK_SECRET} + - POLAR_SUBSCRIPTION_PLANS=${POLAR_SUBSCRIPTION_PLANS} + - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces + restart: unless-stopped workspace_cockroach: image: hardcoreeng/workspace extra_hosts: @@ -279,6 +296,7 @@ services: - DESKTOP_UPDATES_CHANNELS=dev;tracex:dev-tracex - BRANDING_URL=http://huly.local:8087/branding.json - STREAM_URL=http://huly.local:1080/recording + - PAYMENT_URL=http://huly.local:3040 - COMMUNICATION_API_ENABLED=true - BACKUP_URL=http://huly.local:4039/api/backup, - EXCLUDED_APPLICATIONS_FOR_ANONYMOUS=["chunter", "notification"] diff --git a/dev/prod/public/config.json b/dev/prod/public/config.json index 0a43af504a..b2c8376eef 100644 --- a/dev/prod/public/config.json +++ b/dev/prod/public/config.json @@ -22,6 +22,7 @@ "STATS_URL": "http://huly.local:4900", "PASSWORD_STRICTNESS": "none", "STREAM_URL": "http://huly.local:1080/recording", + "PAYMENT_URL": "http://huly.local:3040", "PUBLIC_SCHEDULE_URL": "http://huly.local:8060", "CALDAV_SERVER_URL": "http://huly.local:9070", "EXPORT_URL": "http://huly.local:4009", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 445605fe2c..06e39598a7 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -201,6 +201,7 @@ export interface Config { MAIL_URL?: string COMMUNICATION_API_ENABLED?: string BILLING_URL?: string + PAYMENT_URL?: string EXCLUDED_APPLICATIONS_FOR_ANONYMOUS?: string PULSE_URL?: string HULYLAKE_URL?: string @@ -514,6 +515,7 @@ export async function configurePlatform () { setMetadata(exportPlugin.metadata.ExportUrl, config.EXPORT_URL ?? '') setMetadata(billingPlugin.metadata.BillingURL, config.BILLING_URL ?? '') + setMetadata(presentation.metadata.PaymentUrl, config.PAYMENT_URL ?? '') setMetadata(presentation.metadata.PulseUrl, config.PULSE_URL) setMetadata(presentation.metadata.HulylakeUrl, config.HULYLAKE_URL ?? '') diff --git a/dev/storybook/package.json b/dev/storybook/package.json index c19137c3ab..eb99636ec8 100644 --- a/dev/storybook/package.json +++ b/dev/storybook/package.json @@ -11,7 +11,7 @@ "test": "echo \"No test specified\"" }, "devDependencies": { - "@hcengineering/platform": "^0.7.5", + "@hcengineering/platform": "^0.7.17", "@hcengineering/theme": "^0.7.0", "@hcengineering/ui": "^0.7.0", "@storybook/addon-essentials": "^7.0.6", diff --git a/dev/tool/package.json b/dev/tool/package.json index 3b0bb5521a..6890797894 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -95,7 +95,7 @@ "@hcengineering/model-activity": "^0.7.0", "@hcengineering/model-lead": "^0.7.0", "@hcengineering/postgres": "^0.7.15", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/mongo": "^0.7.15", "@hcengineering/platform": "^0.7.17", "@hcengineering/recruit": "^0.7.0", diff --git a/models/billing/package.json b/models/billing/package.json index 5144c8f55e..a4262c60f5 100644 --- a/models/billing/package.json +++ b/models/billing/package.json @@ -38,6 +38,7 @@ "@hcengineering/model": "^0.7.17", "@hcengineering/model-core": "^0.7.0", "@hcengineering/setting": "^0.7.0", - "@hcengineering/billing": "^0.7.0" + "@hcengineering/billing": "^0.7.0", + "@hcengineering/platform": "^0.7.17" } } diff --git a/models/billing/src/index.ts b/models/billing/src/index.ts index cfae75c466..6fb1a36c6a 100644 --- a/models/billing/src/index.ts +++ b/models/billing/src/index.ts @@ -13,23 +13,98 @@ // limitations under the License. // -import { type Builder } from '@hcengineering/model' -import core from '@hcengineering/model-core' +import { Model, UX, type Builder } from '@hcengineering/model' +import core, { TDoc } from '@hcengineering/model-core' +import { type IntlString } from '@hcengineering/platform' import setting from '@hcengineering/setting' -import billingPlugin from '@hcengineering/billing' -import { AccountRole } from '@hcengineering/core' +import billing, { type Tier } from '@hcengineering/billing' +import { AccountRole, DOMAIN_MODEL } from '@hcengineering/core' export { billingId } from '@hcengineering/billing' -export { billingPlugin as default } +export { billing as default } + +@Model(billing.class.Tier, core.class.Doc, DOMAIN_MODEL) +@UX(billing.string.Tier) +export class TTier extends TDoc implements Tier { + label!: IntlString + description!: IntlString + storageLimitGB!: number + trafficLimitGB!: number + + priceMonthly!: number + index!: number + color?: string +} export function createModel (builder: Builder): void { + builder.createModel(TTier) + builder.createDoc(setting.class.WorkspaceSettingCategory, core.space.Model, { name: 'billing', - label: billingPlugin.string.Billing, - icon: billingPlugin.icon.Billing, - component: billingPlugin.component.Settings, + label: billing.string.Billing, + icon: billing.icon.Billing, + component: billing.component.Settings, group: 'settings-editor', role: AccountRole.Owner, order: 920 }) + + builder.createDoc( + billing.class.Tier, + core.space.Model, + { + label: billing.string.Common, + description: billing.string.CommonDescription, + storageLimitGB: 10, + trafficLimitGB: 10, + priceMonthly: 0, + index: 0 + }, + billing.tier.Common + ) + + builder.createDoc( + billing.class.Tier, + core.space.Model, + { + label: billing.string.Rare, + description: billing.string.RareDescription, + storageLimitGB: 100, + trafficLimitGB: 100, + priceMonthly: 19.99, + index: 1, + color: 'Sky' + }, + billing.tier.Rare + ) + + builder.createDoc( + billing.class.Tier, + core.space.Model, + { + label: billing.string.Epic, + description: billing.string.EpicDescription, + storageLimitGB: 1000, + trafficLimitGB: 500, + priceMonthly: 99.99, + index: 2, + color: 'Orchid' + }, + billing.tier.Epic + ) + + builder.createDoc( + billing.class.Tier, + core.space.Model, + { + label: billing.string.Legendary, + description: billing.string.LegendaryDescription, + storageLimitGB: 10000, + trafficLimitGB: 2000, + priceMonthly: 399.99, + index: 3, + color: 'Orange' + }, + billing.tier.Legendary + ) } diff --git a/models/calendar/package.json b/models/calendar/package.json index 0179aaeeb2..9b456368d5 100644 --- a/models/calendar/package.json +++ b/models/calendar/package.json @@ -48,7 +48,7 @@ "@hcengineering/model-view": "^0.7.0", "@hcengineering/model-setting": "^0.7.0", "@hcengineering/model-workbench": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/activity": "^0.7.0", "@hcengineering/workbench": "^0.7.0", "@hcengineering/model-preference": "^0.7.0", diff --git a/packages/integration-client/package.json b/packages/integration-client/package.json index 0407e3718f..7d830f8f15 100644 --- a/packages/integration-client/package.json +++ b/packages/integration-client/package.json @@ -42,7 +42,7 @@ "@types/uuid": "^8.3.1" }, "dependencies": { - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/core": "^0.7.18", "@hcengineering/platform": "^0.7.17", "@hcengineering/kvs-client": "^0.7.0", diff --git a/packages/payment-client/.eslintrc.js b/packages/payment-client/.eslintrc.js new file mode 100644 index 0000000000..72235dc283 --- /dev/null +++ b/packages/payment-client/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + extends: ['./node_modules/@hcengineering/platform-rig/profiles/default/eslint.config.json'], + parserOptions: { + tsconfigRootDir: __dirname, + project: './tsconfig.json' + } +} diff --git a/packages/payment-client/.npmignore b/packages/payment-client/.npmignore new file mode 100644 index 0000000000..e3ec093c38 --- /dev/null +++ b/packages/payment-client/.npmignore @@ -0,0 +1,4 @@ +* +!/lib/** +!CHANGELOG.md +/lib/**/__tests__/ diff --git a/packages/payment-client/config/rig.json b/packages/payment-client/config/rig.json new file mode 100644 index 0000000000..0110930f55 --- /dev/null +++ b/packages/payment-client/config/rig.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + "rigPackageName": "@hcengineering/platform-rig" +} diff --git a/packages/payment-client/jest.config.js b/packages/payment-client/jest.config.js new file mode 100644 index 0000000000..2cfd408b67 --- /dev/null +++ b/packages/payment-client/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + roots: ["./src"], + coverageReporters: ["text-summary", "html"] +} diff --git a/packages/payment-client/package.json b/packages/payment-client/package.json new file mode 100644 index 0000000000..a40b2746b0 --- /dev/null +++ b/packages/payment-client/package.json @@ -0,0 +1,57 @@ +{ + "name": "@hcengineering/payment-client", + "version": "0.7.0", + "main": "lib/index.js", + "svelte": "src/index.ts", + "types": "types/index.d.ts", + "files": [ + "lib/**/*", + "types/**/*", + "tsconfig.json" + ], + "author": "Hardcore Engineering Inc.", + "license": "EPL-2.0", + "scripts": { + "build": "compile", + "build:watch": "compile", + "format": "format src", + "test": "jest --passWithNoTests --silent", + "_phase:build": "compile transpile src", + "_phase:test": "jest --passWithNoTests --silent", + "_phase:format": "format src", + "_phase:validate": "compile validate" + }, + "devDependencies": { + "cross-env": "~7.0.3", + "@hcengineering/platform-rig": "^0.7.19", + "@types/node": "^22.15.29", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-n": "^15.4.0", + "eslint": "^8.54.0", + "esbuild": "^0.25.9", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-standard-with-typescript": "^40.0.0", + "prettier": "^3.6.2", + "typescript": "^5.9.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", + "@types/jest": "^29.5.5" + }, + "dependencies": { + "@hcengineering/core": "^0.7.18", + "@hcengineering/platform": "^0.7.17" + }, + "repository": "https://github.com/hcengineering/platform", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": { + "types": "./types/index.d.ts", + "require": "./lib/index.js", + "import": "./lib/index.js" + } + } +} diff --git a/packages/payment-client/src/client.ts b/packages/payment-client/src/client.ts new file mode 100644 index 0000000000..02b6cf753a --- /dev/null +++ b/packages/payment-client/src/client.ts @@ -0,0 +1,175 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { concatLink, type WorkspaceUuid } from '@hcengineering/core' +import { CheckoutResponse, SubscribeRequest, CheckoutStatus, SubscriptionData } from './types' +import { PaymentError, NetworkError } from './error' + +/** + * Create a payment client instance + * @param paymentUrl - URL of the payment service + * @param token - Authentication token + * @returns PaymentClient instance + */ +export function getClient (paymentUrl?: string, token?: string): PaymentClient { + if (paymentUrl === undefined || paymentUrl == null || paymentUrl === '') { + throw new Error('Payment service URL not specified') + } + if (token === undefined || token == null || token === '') { + throw new Error('Authentication token not specified') + } + + return new PaymentClient(paymentUrl, token) +} + +/** + * Payment service client + * Handles all subscription and payment operations + */ +export class PaymentClient { + private readonly headers: Record + + constructor ( + private readonly endpoint: string, + private readonly token: string + ) { + this.headers = { + Authorization: 'Bearer ' + token, + 'Content-Type': 'application/json' + } + } + + /** + * Create a subscription for a workspace + * @param workspace - Workspace UUID + * @param request - Subscription request details + * @returns Checkout details with URL for payment + */ + async createSubscription (workspace: WorkspaceUuid, request: SubscribeRequest): Promise { + const path = `/api/v1/subscriptions/${workspace}/subscribe` + const url = new URL(concatLink(this.endpoint, path)) + const body = JSON.stringify(request) + const response = await fetchSafe(url, { + method: 'POST', + headers: { ...this.headers }, + body + }) + return (await response.json()) as CheckoutResponse + } + + /** + * Get subscription details + * @param subscriptionId - Subscription ID + * @returns Subscription details from payment provider + */ + async getSubscription (subscriptionId: string): Promise { + const path = `/api/v1/subscriptions/${subscriptionId}` + const url = new URL(concatLink(this.endpoint, path)) + const response = await fetchSafe(url, { headers: { ...this.headers } }) + return await response.json() + } + + /** + * Cancel a subscription + * @param subscriptionId - Subscription ID to cancel + * @returns Cancellation confirmation + */ + async cancelSubscription (subscriptionId: string): Promise { + const path = `/api/v1/subscriptions/${subscriptionId}/cancel` + const url = new URL(concatLink(this.endpoint, path)) + const response = await fetchSafe(url, { + method: 'POST', + headers: { ...this.headers } + }) + return (await response.json()) as SubscriptionData + } + + /** + * Uncancel a subscription (reactivate a previously canceled subscription) + * @param subscriptionId - Subscription ID to uncancel + * @returns Reactivation confirmation + */ + async uncancelSubscription (subscriptionId: string): Promise { + const path = `/api/v1/subscriptions/${subscriptionId}/uncancel` + const url = new URL(concatLink(this.endpoint, path)) + const response = await fetchSafe(url, { + method: 'POST', + headers: { ...this.headers } + }) + return (await response.json()) as SubscriptionData + } + + /** + * Update a subscription to a different plan + * For free-to-paid upgrades, returns CheckoutResponse (requires checkout) + * For paid-to-paid updates, returns SubscriptionData (direct update) + * @param subscriptionId - Subscription ID to update + * @param plan - New plan name + * @returns CheckoutResponse for free-to-paid upgrades or updated SubscriptionData for direct updates + */ + async updateSubscriptionPlan (subscriptionId: string, plan: string): Promise { + const path = `/api/v1/subscriptions/${subscriptionId}/updatePlan` + const url = new URL(concatLink(this.endpoint, path)) + const body = JSON.stringify({ plan }) + const response = await fetchSafe(url, { + method: 'POST', + headers: { ...this.headers }, + body + }) + return (await response.json()) as SubscriptionData | CheckoutResponse + } + + /** + * Get checkout status + * Poll this endpoint after user returns from payment provider to check if subscription is ready + * @param checkoutId - Checkout ID returned from createSubscription + * @returns Checkout status with subscription details if completed + */ + async getCheckoutStatus (checkoutId: string): Promise { + const path = `/api/v1/checkouts/${checkoutId}/status` + const url = new URL(concatLink(this.endpoint, path)) + const response = await fetchSafe(url, { headers: { ...this.headers } }) + return (await response.json()) as CheckoutStatus + } +} + +/** + * Safe fetch wrapper that handles errors consistently + * @param url - URL to fetch + * @param init - Fetch options + * @returns Response + * @throws NetworkError on network issues + * @throws PaymentError on non-ok responses + */ +async function fetchSafe (url: string | URL, init?: RequestInit): Promise { + let response + try { + response = await fetch(url, init) + } catch (err: any) { + throw new NetworkError(`Network error: ${String(err)}`) + } + + if (!response.ok) { + const text = await response.text() + try { + const error = JSON.parse(text) + throw new PaymentError(error.error ?? text) + } catch { + throw new PaymentError(`Payment service error: ${response.status} ${text}`) + } + } + + return response +} diff --git a/packages/payment-client/src/error.ts b/packages/payment-client/src/error.ts new file mode 100644 index 0000000000..782e6aa89c --- /dev/null +++ b/packages/payment-client/src/error.ts @@ -0,0 +1,30 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/** Error for payment service errors */ +export class PaymentError extends Error { + constructor (message: string) { + super(message) + this.name = 'PaymentError' + } +} + +/** Error for network/connectivity issues */ +export class NetworkError extends Error { + constructor (message: string) { + super(message) + this.name = 'NetworkError' + } +} diff --git a/packages/payment-client/src/index.ts b/packages/payment-client/src/index.ts new file mode 100644 index 0000000000..07d3c30d38 --- /dev/null +++ b/packages/payment-client/src/index.ts @@ -0,0 +1,3 @@ +export * from './client' +export * from './types' +export * from './error' diff --git a/packages/payment-client/src/types.ts b/packages/payment-client/src/types.ts new file mode 100644 index 0000000000..b3c4d14727 --- /dev/null +++ b/packages/payment-client/src/types.ts @@ -0,0 +1,84 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { AccountUuid, WorkspaceUuid } from '@hcengineering/core' + +export enum SubscriptionType { + Tier = 'tier', // Main workspace tier (free, starter, pro, enterprise) + Support = 'support' // Voluntary support/donation subscription +} + +export enum SubscriptionStatus { + Active = 'active', // Subscription is active and paid + Trialing = 'trialing', // In trial period (free usage) + PastDue = 'past_due', // Payment failed but subscription not yet canceled + Canceled = 'canceled', // Subscription was canceled by user or admin + Paused = 'paused', // Subscription is temporarily paused (some providers support this) + Expired = 'expired' // Subscription or trial has expired +} + +/** + * Subscription request parameters + * Used when creating a new subscription + */ +export interface SubscribeRequest { + type: SubscriptionType + plan: string // Plan identifier + customerEmail?: string // Optional customer email + customerName?: string // Optional customer name +} + +/** + * Checkout creation response + * Contains checkout details for payment + */ +export interface CheckoutResponse { + checkoutId: string // Checkout session ID + checkoutUrl: string // URL to redirect user to for payment +} + +/** + * Subscription data for checkout status + * Matches @hcengineering/account-client Subscription type + * @see @hcengineering/account-client + */ +export interface SubscriptionData { + id: string // Internal unique subscription ID + workspaceUuid: WorkspaceUuid + accountUuid: AccountUuid + provider: string + providerSubscriptionId: string + providerCheckoutId?: string + type: SubscriptionType + status: SubscriptionStatus + plan: string + amount?: number + periodStart?: number + periodEnd?: number + trialEnd?: number + canceledAt?: number + providerData?: Record +} + +/** + * Checkout status response + * Contains information about the checkout and subscription status + */ +export interface CheckoutStatus { + checkoutId: string // Checkout session ID + subscriptionId: string | null // Subscription ID if completed + status: 'pending' | 'completed' // Checkout status + subscription: SubscriptionData | null // Full subscription data if available +} diff --git a/packages/payment-client/tsconfig.json b/packages/payment-client/tsconfig.json new file mode 100644 index 0000000000..b5ae22f6e4 --- /dev/null +++ b/packages/payment-client/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "lib", "dist", "types", "bundle"] +} \ No newline at end of file diff --git a/packages/presentation/src/plugin.ts b/packages/presentation/src/plugin.ts index 6e5ea0af97..67e4048507 100644 --- a/packages/presentation/src/plugin.ts +++ b/packages/presentation/src/plugin.ts @@ -185,7 +185,8 @@ export default plugin(presentationId, { MailUrl: '' as Metadata, PreviewUrl: '' as Metadata, PulseUrl: '' as Metadata, - HulylakeUrl: '' as Metadata + HulylakeUrl: '' as Metadata, + PaymentUrl: '' as Metadata }, status: { FileTooLarge: '' as StatusCode diff --git a/plugins/ai-assistant-resources/package.json b/plugins/ai-assistant-resources/package.json index 81303cf6c6..40d728975d 100644 --- a/plugins/ai-assistant-resources/package.json +++ b/plugins/ai-assistant-resources/package.json @@ -52,7 +52,7 @@ "@hcengineering/panel": "^0.7.0", "@hcengineering/analytics": "^0.7.17", "@hcengineering/integration-client": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/setting-resources": "^0.7.0", "@hcengineering/view-resources": "^0.7.0" } diff --git a/plugins/billing-assets/assets/icons.svg b/plugins/billing-assets/assets/icons.svg index a9053ba374..3b5f3c588f 100644 --- a/plugins/billing-assets/assets/icons.svg +++ b/plugins/billing-assets/assets/icons.svg @@ -2,4 +2,7 @@ + + + diff --git a/plugins/billing-assets/lang/cs.json b/plugins/billing-assets/lang/cs.json index 86ed0caecd..acf2572749 100644 --- a/plugins/billing-assets/lang/cs.json +++ b/plugins/billing-assets/lang/cs.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Fakturace", + "Subscriptions": "Předplatné", + "AllPlans": "Všechny tarify", + "ActivePlan": "Aktivní tarif", + "ResourceUsage": "Využití zdrojů", "DriveSize": "Velikost souborů", "DriveCount": "Počet souborů", "OfficeSessionsDuration": "Doba schůzek", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Doba záznamu", "AI": "AI", "TranscriptionTime": "Doba přepisu", - "TotalTokens": "Celkem tokenů" + "TotalTokens": "Celkem tokenů", + "Tier": "Tarif", + "StorageLimit": "{limit}GB úložiště", + "TrafficLimit": "{limit}GB video/audio provozu", + "Common": "Common", + "CommonDescription": "Pro jednotlivce a týmy začínající s Huly.", + "Rare": "Rare", + "RareDescription": "Pro jednotlivé kreativce, freelancery a mikro-agentury.", + "Epic": "Epic", + "EpicDescription": "Pro profesionální kreativní společnosti a malé firmy.", + "Legendary": "Legendary", + "LegendaryDescription": "Nejlepší pro velké týmy, které potřebují maximální možnosti.", + "UnlimitedUsers": "Neomezení uživatelé", + "UnlimitedObjects": "Neomezené Huly objekty", + "Upgrade": "Upgradovat", + "Subscribe": "Přihlásit se", + "Monthly": "Měsíčně", + "Acitve": "Aktivní", + "NoActivePlan": "Žádný aktivní plán", + "SelectPlanToBegin": "Chcete-li začít, vyberte plán níže", + "SubscriptionEnds": "Do {date}", + "SubscriptionRenews": "Obnovení: {date}", + "SubscriptionValidUntil": "Platné do {date}", + "ProcessingPayment": "Zpracování platby...", + "Downgrade": "Snížit", + "CancelSubscription": "Zrušit předplatné", + "ConfirmUpgrade": "Upgradovat předplatné?", + "ConfirmDowngrade": "Snížit předplatné?", + "ConfirmCancel": "Zrušit předplatné?", + "UpgradeDescription": "Bude vám účtováno až ${amount} okamžitě. Přesná částka závisí na vašem aktuálním fakturačním období.", + "DowngradeDescription": "Obdržíte kredit až ${amount} za zbývající čas předplatného. Snížení vstoupí v platnost okamžitě.", + "CancelDescription": "Vaše předplatné zůstane aktivní do konce období fakturace.", + "UncancelSubscription": "Reaktivovat předplatné", + "ConfirmUncancel": "Reaktivovat předplatné?", + "UncancelDescription": "Vaše předplatné bude reaktivováno a bude pokračovat od konce aktuálního období fakturace.", + "PriceDifference": "Rozdíl v ceně: {amount}", + "DialogCancel": "Zrušit", + "DialogConfirm": "Potvrdit" } } diff --git a/plugins/billing-assets/lang/de.json b/plugins/billing-assets/lang/de.json index 86befc9986..b207d67146 100644 --- a/plugins/billing-assets/lang/de.json +++ b/plugins/billing-assets/lang/de.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Abrechnung", + "Subscriptions": "Abonnements", + "AllPlans": "Alle Pläne", + "ActivePlan": "Aktiver Plan", + "ResourceUsage": "Ressourcennutzung", "DriveSize": "Dateigröße", "DriveCount": "Dateianzahl", "OfficeSessionsDuration": "Besprechungsdauer", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Aufzeichnungsdauer", "AI": "AI", "TranscriptionTime": "Transkriptionszeit", - "TotalTokens": "Gesamte Tokens" + "TotalTokens": "Gesamte Tokens", + "Tier": "Tarif", + "StorageLimit": "{limit} {unit} Speicher", + "TrafficLimit": "{limit} {unit} Video/Audio-Traffic", + "Common": "Common", + "CommonDescription": "Für Einzelpersonen und Teams, die mit Huly beginnen.", + "Rare": "Rare", + "RareDescription": "Für kreative Einzelpersonen, Freelancer und Mikro-Agenturen.", + "Epic": "Epic", + "EpicDescription": "Für professionelle Kreativunternehmen und kleine Unternehmen.", + "Legendary": "Legendary", + "LegendaryDescription": "Am besten für große Teams, die maximale Möglichkeiten benötigen.", + "UnlimitedUsers": "Unbegrenzte Benutzer", + "UnlimitedObjects": "Unbegrenzte Huly-Objekte", + "ChangePlan": "Plan ändern", + "Subscribe": "Abonnieren", + "Monthly": "Monatlich", + "Active": "Aktiv", + "NoActivePlan": "Kein aktiver Plan", + "SelectPlanToBegin": "Wählen Sie unten einen Plan aus, um zu beginnen", + "SubscriptionEnds": "Bis {date}", + "SubscriptionRenews": "Nächste Erneuerung: {date}", + "SubscriptionValidUntil": "Gültig bis {date}", + "ProcessingPayment": "Zahlung wird verarbeitet...", + "Downgrade": "Herabstufen", + "CancelSubscription": "Abonnement kündigen", + "ConfirmUpgrade": "Abonnement aktualisieren?", + "ConfirmDowngrade": "Abonnement herabstufen?", + "ConfirmCancel": "Abonnement kündigen?", + "UpgradeDescription": "Ihnen wird sofort bis zu ${amount} berechnet. Der genaue Betrag hängt von Ihrem aktuellen Abrechnungszeitraum ab.", + "DowngradeDescription": "Sie erhalten eine Gutschrift von bis zu ${amount} für die verbleibende Abonnementlaufzeit. Die Herabstufung wird sofort wirksam.", + "CancelDescription": "Ihr Abonnement bleibt bis zum Ende des Abonnementzeitraums aktiv.", + "UncancelSubscription": "Abonnement reaktivieren", + "ConfirmUncancel": "Abonnement reaktivieren?", + "UncancelDescription": "Ihr Abonnement wird reaktiviert und läuft ab dem Ende des aktuellen Abrechnungszeitraums weiter.", + "PriceDifference": "Preisdifferenz: {amount}", + "DialogCancel": "Abbrechen", + "DialogConfirm": "Bestätigen" } } diff --git a/plugins/billing-assets/lang/en.json b/plugins/billing-assets/lang/en.json index 2d277cc69b..82656316d7 100644 --- a/plugins/billing-assets/lang/en.json +++ b/plugins/billing-assets/lang/en.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Billing", + "Subscriptions": "Subscriptions", + "AllPlans": "All plans", + "ActivePlan": "Active plan", + "ResourceUsage": "Resource usage", "DriveSize": "Files size", "DriveCount": "Files count", "OfficeSessionsDuration": "Meetings time", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Recording time", "AI": "AI", "TranscriptionTime": "Transcription time", - "TotalTokens": "Total tokens" + "TotalTokens": "Total tokens", + "Tier": "Tier", + "StorageLimit": "{limit} {unit} storage", + "TrafficLimit": "{limit} {unit} video/audio traffic", + "Common": "Common", + "CommonDescription": "For individuals and teams getting started with Huly.", + "Rare": "Rare", + "RareDescription": "For individual creatives, freelancers, and micro-agencies.", + "Epic": "Epic", + "EpicDescription": "For professional creative companies and small businesses.", + "Legendary": "Legendary", + "LegendaryDescription": "Best for large multiple teams that need maximum capabilities.", + "UnlimitedUsers": "Unlimited users", + "UnlimitedObjects": "Unlimited Huly objects", + "ChangePlan": "Change plan", + "Subscribe": "Subscribe", + "Monthly": "Monthly", + "Active": "Active", + "NoActivePlan": "No active plan", + "SelectPlanToBegin": "Select a plan below to get started", + "SubscriptionEnds": "Until {date}", + "SubscriptionRenews": "Next renewal: {date}", + "SubscriptionValidUntil": "Valid until {date}", + "ProcessingPayment": "Processing payment...", + "Downgrade": "Downgrade", + "CancelSubscription": "Cancel Subscription", + "ConfirmUpgrade": "Upgrade subscription?", + "ConfirmDowngrade": "Downgrade subscription?", + "ConfirmCancel": "Cancel subscription?", + "UpgradeDescription": "You will be charged up to ${amount} immediately. The exact amount depends on your current billing period.", + "DowngradeDescription": "You will receive a credit of up to ${amount} for the remaining subscription time. The downgrade takes effect immediately.", + "CancelDescription": "Your subscription will remain active until the end of the billing period.", + "UncancelSubscription": "Reactivate Subscription", + "ConfirmUncancel": "Reactivate subscription?", + "UncancelDescription": "Your subscription will be reactivated and continue from the end of the current billing period.", + "PriceDifference": "Price difference: {amount}", + "DialogCancel": "Cancel", + "DialogConfirm": "Confirm" } } diff --git a/plugins/billing-assets/lang/es.json b/plugins/billing-assets/lang/es.json index d8ad2d2f7a..abe7d0c993 100644 --- a/plugins/billing-assets/lang/es.json +++ b/plugins/billing-assets/lang/es.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Facturación", + "Subscriptions": "Suscripciones", + "AllPlans": "Todos los planes", + "ActivePlan": "Plan activo", + "ResourceUsage": "Uso de recursos", "DriveSize": "Tamaño de archivos", "DriveCount": "Cantidad de archivos", "OfficeSessionsDuration": "Tiempo de reuniones", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Tiempo de grabación", "AI": "IA", "TranscriptionTime": "Tiempo de transcripción", - "TotalTokens": "Total de tokens" + "TotalTokens": "Total de tokens", + "Tier": "Plan", + "StorageLimit": "{limit} {unit} de almacenamiento", + "TrafficLimit": "{limit} {unit} de tráfico de video/audio", + "Common": "Common", + "CommonDescription": "Para individuos y equipos que comienzan con Huly.", + "Rare": "Rare", + "RareDescription": "Para creativos individuales, freelancers y micro-agencias.", + "Epic": "Epic", + "EpicDescription": "Para empresas creativas profesionales y pequeñas empresas.", + "Legendary": "Legendary", + "LegendaryDescription": "Lo mejor para equipos grandes que necesitan capacidades máximas.", + "UnlimitedUsers": "Usuarios ilimitados", + "UnlimitedObjects": "Objetos Huly ilimitados", + "ChangePlan": "Cambiar plan", + "Subscribe": "Suscribirse", + "Monthly": "Mensual", + "Active": "Activo", + "SubscriptionEnds": "Hasta {date}", + "SubscriptionRenews": "Próxima renovación: {date}", + "SubscriptionValidUntil": "Válido hasta {date}", + "ProcessingPayment": "Procesando pago...", + "Downgrade": "Reducir", + "CancelSubscription": "Cancelar suscripción", + "ConfirmUpgrade": "¿Mejorar suscripción?", + "ConfirmDowngrade": "¿Reducir suscripción?", + "ConfirmCancel": "¿Cancelar suscripción?", + "UpgradeDescription": "Se le cobrará hasta ${amount} inmediatamente. La cantidad exacta depende de su período de facturación actual.", + "DowngradeDescription": "Recibirá un crédito de hasta ${amount} por el tiempo de suscripción restante. La reducción entra en vigor inmediatamente.", + "CancelDescription": "Su suscripción permanecerá activa hasta el final del período de facturación.", + "UncancelSubscription": "Reactivar suscripción", + "ConfirmUncancel": "¿Reactivar suscripción?", + "UncancelDescription": "Su suscripción se reactivará y continuará desde el final del período de facturación actual.", + "PriceDifference": "Diferencia de precio: {amount}", + "DialogCancel": "Cancelar", + "DialogConfirm": "Confirmar", + "NoActivePlan": "Sin plan activo", + "SelectPlanToBegin": "Selecciona un plan a continuación para comenzar" } } diff --git a/plugins/billing-assets/lang/fr.json b/plugins/billing-assets/lang/fr.json index facccb5b1e..899655c998 100644 --- a/plugins/billing-assets/lang/fr.json +++ b/plugins/billing-assets/lang/fr.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Facturation", + "Subscriptions": "Abonnements", + "AllPlans": "Tous les plans", + "ActivePlan": "Plan actif", + "ResourceUsage": "Utilisation des ressources", "DriveSize": "Taille des fichiers", "DriveCount": "Nombre de fichiers", "OfficeSessionsDuration": "Durée des réunions", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Durée d'enregistrement", "AI": "IA", "TranscriptionTime": "Durée de transcription", - "TotalTokens": "Nombre total de jetons" + "TotalTokens": "Nombre total de jetons", + "Tier": "Plan", + "StorageLimit": "{limit} {unit} de stockage", + "TrafficLimit": "{limit} {unit} de trafic vidéo/audio", + "Common": "Common", + "CommonDescription": "Pour les particuliers et les équipes qui débutent avec Huly.", + "Rare": "Rare", + "RareDescription": "Pour les créateurs individuels, freelancers et micro-agences.", + "Epic": "Epic", + "EpicDescription": "Pour les entreprises créatives professionnelles et les petites entreprises.", + "Legendary": "Legendary", + "LegendaryDescription": "Idéal pour les grandes équipes qui nécessitent des capacités maximales.", + "UnlimitedUsers": "Utilisateurs illimités", + "UnlimitedObjects": "Objets Huly illimités", + "ChangePlan": "Changer de plan", + "Subscribe": "S'abonner", + "Monthly": "Mensuel", + "Active": "Actif", + "NoActivePlan": "Aucun plan actif", + "SelectPlanToBegin": "Sélectionnez un plan ci-dessous pour commencer", + "SubscriptionEnds": "Jusqu'au {date}", + "SubscriptionRenews": "Prochain renouvellement: {date}", + "SubscriptionValidUntil": "Valide jusqu'au {date}", + "ProcessingPayment": "Traitement du paiement...", + "Downgrade": "Rétrograder", + "CancelSubscription": "Annuler l'abonnement", + "ConfirmUpgrade": "Mettre à niveau l'abonnement?", + "ConfirmDowngrade": "Rétrograder l'abonnement?", + "ConfirmCancel": "Annuler l'abonnement?", + "UpgradeDescription": "Vous serez facturé jusqu'à ${amount} immédiatement. Le montant exact dépend de votre période de facturation actuelle.", + "DowngradeDescription": "Vous recevrez un crédit jusqu'à ${amount} pour le temps d'abonnement restant. La rétrogradation prend effet immédiatement.", + "CancelDescription": "Votre abonnement reste actif jusqu'à la fin de la période de facturation.", + "UncancelSubscription": "Réactiver l'abonnement", + "ConfirmUncancel": "Réactiver l'abonnement?", + "UncancelDescription": "Votre abonnement sera réactivé et se poursuivra à partir de la fin de la période de facturation actuelle.", + "PriceDifference": "Différence de prix: {amount}", + "DialogCancel": "Annuler", + "DialogConfirm": "Confirmer" } } diff --git a/plugins/billing-assets/lang/it.json b/plugins/billing-assets/lang/it.json index b62c19cb38..69506f44d0 100644 --- a/plugins/billing-assets/lang/it.json +++ b/plugins/billing-assets/lang/it.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Fatturazione", + "Subscriptions": "Abbonamenti", + "AllPlans": "Tutti i piani", + "ActivePlan": "Piano attivo", + "ResourceUsage": "Utilizzo risorse", "DriveSize": "Dimensione file", "DriveCount": "Conteggio file", "OfficeSessionsDuration": "Durata riunioni", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Durata registrazione", "AI": "AI", "TranscriptionTime": "Tempo di trascrizione", - "TotalTokens": "Totale token" + "TotalTokens": "Totale token", + "Tier": "Piano", + "StorageLimit": "{limit} {unit} di archiviazione", + "TrafficLimit": "{limit} {unit} di traffico video/audio", + "Common": "Common", + "CommonDescription": "Per singoli e team che iniziano con Huly.", + "Rare": "Rare", + "RareDescription": "Per creativi individuali, freelancer e micro-agenzie.", + "Epic": "Epic", + "EpicDescription": "Per aziende creative professionali e piccole imprese.", + "Legendary": "Legendary", + "LegendaryDescription": "Migliore per grandi team che necessitano di capacità massime.", + "UnlimitedUsers": "Utenti illimitati", + "UnlimitedObjects": "Oggetti Huly illimitati", + "ChangePlan": "Cambia piano", + "Subscribe": "Sottoscrivi", + "Monthly": "Mensile", + "Active": "Attivo", + "NoActivePlan": "Nessun piano attivo", + "SelectPlanToBegin": "Seleziona un piano di seguito per iniziare", + "SubscriptionEnds": "Fino al {date}", + "SubscriptionRenews": "Prossimo rinnovo: {date}", + "SubscriptionValidUntil": "Valido fino al {date}", + "ProcessingPayment": "Elaborazione pagamento...", + "Downgrade": "Downgrade", + "CancelSubscription": "Annulla abbonamento", + "ConfirmUpgrade": "Aggiornare l'abbonamento?", + "ConfirmDowngrade": "Eseguire il downgrade dell'abbonamento?", + "ConfirmCancel": "Annullare l'abbonamento?", + "UpgradeDescription": "Ti verrà addebitato fino a ${amount} immediatamente. L'importo esatto dipende dal tuo periodo di fatturazione attuale.", + "DowngradeDescription": "Riceverai un credito fino a ${amount} per il tempo di abbonamento rimanente. Il downgrade ha effetto immediato.", + "CancelDescription": "Il tuo abbonamento rimane attivo fino alla fine del periodo di fatturazione.", + "UncancelSubscription": "Riattiva abbonamento", + "ConfirmUncancel": "Riattivare l'abbonamento?", + "UncancelDescription": "Il tuo abbonamento verrà riattivato e continuerà dalla fine del periodo di fatturazione attuale.", + "PriceDifference": "Differenza di prezzo: {amount}", + "DialogCancel": "Annulla", + "DialogConfirm": "Conferma" } } diff --git a/plugins/billing-assets/lang/ja.json b/plugins/billing-assets/lang/ja.json index d5abbc891c..2447e09132 100644 --- a/plugins/billing-assets/lang/ja.json +++ b/plugins/billing-assets/lang/ja.json @@ -1,6 +1,10 @@ { "string": { "Billing": "請求", + "Subscriptions": "サブスクリプション", + "AllPlans": "すべてのプラン", + "ActivePlan": "アクティブなプラン", + "ResourceUsage": "リソース使用状況", "DriveSize": "ファイルサイズ", "DriveCount": "ファイル数", "OfficeSessionsDuration": "会議時間", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "録画時間", "AI": "AI", "TranscriptionTime": "文字起こし時間", - "TotalTokens": "合計トークン" + "TotalTokens": "合計トークン", + "Tier": "プラン", + "StorageLimit": "{limit} {unit} ストレージ", + "TrafficLimit": "{limit} {unit} ビデオ/オーディオトラフィック", + "Common": "Common", + "CommonDescription": "Hulyを始める個人とチーム向け。", + "Rare": "Rare", + "RareDescription": "個人クリエイター、フリーランサー、マイクロエージェンシー向け。", + "Epic": "Epic", + "EpicDescription": "プロのクリエイティブ企業と中小企業向け。", + "Legendary": "Legendary", + "LegendaryDescription": "最大限の機能が必要な大規模チームに最適。", + "UnlimitedUsers": "無制限のユーザー", + "UnlimitedObjects": "無制限のHulyオブジェクト", + "ChangePlan": "プラン変更", + "Subscribe": "購読", + "Monthly": "月額", + "Active": "アクティブ", + "SubscriptionEnds": "{date}まで", + "SubscriptionRenews": "次回更新: {date}", + "SubscriptionValidUntil": "{date}まで有効", + "ProcessingPayment": "お支払い処理中...", + "Downgrade": "ダウングレード", + "CancelSubscription": "サブスクリプションをキャンセル", + "ConfirmUpgrade": "サブスクリプションをアップグレードしますか?", + "ConfirmDowngrade": "サブスクリプションをダウングレードしますか?", + "ConfirmCancel": "サブスクリプションをキャンセルしますか?", + "UpgradeDescription": "最大 ${amount} が即座に請求されます。正確な金額は、現在の請求期間によって異なります。", + "DowngradeDescription": "残りのサブスクリプション時間分の最大 ${amount} のクレジットを受け取ります。ダウングレードは即座に有効になります。", + "CancelDescription": "サブスクリプションは請求期間の終了まで有効なままです。", + "UncancelSubscription": "サブスクリプションを再度有効化", + "ConfirmUncancel": "サブスクリプションを再度有効化しますか?", + "UncancelDescription": "サブスクリプションが再度有効化され、現在の請求期間の終了から継続します。", + "PriceDifference": "価格差: {amount}", + "DialogCancel": "キャンセル", + "DialogConfirm": "確認", + "NoActivePlan": "アクティブなプランなし", + "SelectPlanToBegin": "下記からプランを選択して開始してください" } } diff --git a/plugins/billing-assets/lang/pt.json b/plugins/billing-assets/lang/pt.json index 50c416f102..8b9e071912 100644 --- a/plugins/billing-assets/lang/pt.json +++ b/plugins/billing-assets/lang/pt.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Faturamento", + "Subscriptions": "Assinaturas", + "AllPlans": "Todos os planos", + "ActivePlan": "Plano ativo", + "ResourceUsage": "Uso de recursos", "DriveSize": "Tamanho dos arquivos", "DriveCount": "Quantidade de arquivos", "OfficeSessionsDuration": "Tempo de reuniões", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Tempo de gravação", "AI": "IA", "TranscriptionTime": "Tempo de transcrição", - "TotalTokens": "Total de tokens" + "TotalTokens": "Total de tokens", + "Tier": "Plano", + "StorageLimit": "{limit} {unit} de armazenamento", + "TrafficLimit": "{limit} {unit} de tráfego de vídeo/áudio", + "Common": "Common", + "CommonDescription": "Para indivíduos e equipes começando com Huly.", + "Rare": "Rare", + "RareDescription": "Para criativos individuais, freelancers e micro-agências.", + "Epic": "Epic", + "EpicDescription": "Para empresas criativas profissionais e pequenas empresas.", + "Legendary": "Legendary", + "LegendaryDescription": "Melhor para grandes equipes que precisam de capacidades máximas.", + "UnlimitedUsers": "Usuários ilimitados", + "UnlimitedObjects": "Objetos Huly ilimitados", + "ChangePlan": "Alterar plano", + "Subscribe": "Assinar", + "Monthly": "Mensal", + "Active": "Ativo", + "NoActivePlan": "Nenhum plano ativo", + "SelectPlanToBegin": "Selecione um plano abaixo para começar", + "SubscriptionEnds": "Até {date}", + "SubscriptionRenews": "Próxima renovação: {date}", + "SubscriptionValidUntil": "Válido até {date}", + "ProcessingPayment": "Processando pagamento...", + "Downgrade": "Fazer downgrade", + "CancelSubscription": "Cancelar assinatura", + "ConfirmUpgrade": "Atualizar assinatura?", + "ConfirmDowngrade": "Fazer downgrade da assinatura?", + "ConfirmCancel": "Cancelar assinatura?", + "UpgradeDescription": "Você será cobrado até ${amount} imediatamente. O valor exato depende de seu período de faturamento atual.", + "DowngradeDescription": "Você receberá um crédito de até ${amount} pelo tempo de assinatura restante. O downgrade entra em vigor imediatamente.", + "CancelDescription": "Sua assinatura permanecerá ativa até o final do período de faturamento.", + "UncancelSubscription": "Reativar assinatura", + "ConfirmUncancel": "Reativar assinatura?", + "UncancelDescription": "Sua assinatura será reativada e continuará a partir do final do período de faturamento atual.", + "PriceDifference": "Diferença de preço: {amount}", + "DialogCancel": "Cancelar", + "DialogConfirm": "Confirmar" } } diff --git a/plugins/billing-assets/lang/ru.json b/plugins/billing-assets/lang/ru.json index c69d491fa1..ccda370ba9 100644 --- a/plugins/billing-assets/lang/ru.json +++ b/plugins/billing-assets/lang/ru.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Биллинг", + "Subscriptions": "Подписки", + "AllPlans": "Все тарифы", + "ActivePlan": "Активный тариф", + "ResourceUsage": "Использование ресурсов", "DriveSize": "Размер файлов", "DriveCount": "Количество файлов", "OfficeSessionsDuration": "Время встреч", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Время записи", "AI": "AI", "TranscriptionTime": "Время транскрипции", - "TotalTokens": "Всего токенов" + "TotalTokens": "Всего токенов", + "Tier": "Тариф", + "StorageLimit": "{limit} {unit} хранилища", + "TrafficLimit": "{limit} {unit} видео/аудио трафика", + "Common": "Common", + "CommonDescription": "Для частных лиц и команд, начинающих работу с Huly.", + "Rare": "Rare", + "RareDescription": "Для индивидуальных творческих работников, фрилансеров и микро-агентств.", + "Epic": "Epic", + "EpicDescription": "Для профессиональных творческих компаний и малого бизнеса.", + "Legendary": "Legendary", + "LegendaryDescription": "Лучший вариант для больших команд, которым нужны максимальные возможности.", + "UnlimitedUsers": "Неограниченное количество пользователей", + "UnlimitedObjects": "Неограниченное количество объектов Huly", + "ChangePlan": "Сменить план", + "Subscribe": "Подписаться", + "Monthly": "В месяц", + "Active": "Активен", + "NoActivePlan": "Нет активного плана", + "SelectPlanToBegin": "Выберите план ниже, чтобы начать", + "SubscriptionEnds": "До {date}", + "SubscriptionRenews": "Следующее обновление: {date}", + "SubscriptionValidUntil": "Действительно до {date}", + "ProcessingPayment": "Обработка платежа...", + "Downgrade": "Понизить", + "CancelSubscription": "Отменить подписку", + "ConfirmUpgrade": "Обновить подписку?", + "ConfirmDowngrade": "Понизить подписку?", + "ConfirmCancel": "Отменить подписку?", + "UpgradeDescription": "С вас будет взято до ${amount} немедленно. Точная сумма зависит от вашего текущего периода выставления счета.", + "DowngradeDescription": "Вы получите кредит до ${amount} за оставшееся время подписки. Понижение вступает в силу немедленно.", + "CancelDescription": "Ваша подписка останется активной до конца периода выставления счета.", + "UncancelSubscription": "Переактивировать подписку", + "ConfirmUncancel": "Переактивировать подписку?", + "UncancelDescription": "Ваша подписка будет переактивирована и продолжится с конца текущего периода выставления счета.", + "PriceDifference": "Разница в цене: {amount}", + "DialogCancel": "Отменить", + "DialogConfirm": "Подтвердить" } } diff --git a/plugins/billing-assets/lang/tr.json b/plugins/billing-assets/lang/tr.json index d6576199f7..718d8a023d 100644 --- a/plugins/billing-assets/lang/tr.json +++ b/plugins/billing-assets/lang/tr.json @@ -1,6 +1,10 @@ { "string": { "Billing": "Faturalama", + "Subscriptions": "Abonelikler", + "AllPlans": "Tüm planlar", + "ActivePlan": "Aktif plan", + "ResourceUsage": "Kaynak kullanımı", "DriveSize": "Dosya boyutu", "DriveCount": "Dosya sayısı", "OfficeSessionsDuration": "Toplantı süresi", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "Kayıt süresi", "AI": "AI", "TranscriptionTime": "Transkripsiyon süresi", - "TotalTokens": "Toplam belirteçler" + "TotalTokens": "Toplam belirteçler", + "Tier": "Plan", + "StorageLimit": "{limit} {unit} depolama", + "TrafficLimit": "{limit} {unit} video/ses trafiği", + "Common": "Common", + "CommonDescription": "Huly ile başlayan bireyler ve ekipler için.", + "Rare": "Rare", + "RareDescription": "Bireysel yaratıcılar, serbest çalışanlar ve mikro ajanslar için.", + "Epic": "Epic", + "EpicDescription": "Profesyonel yaratıcı şirketler ve küçük işletmeler için.", + "Legendary": "Legendary", + "LegendaryDescription": "Maksimum yeteneklere ihtiyaç duyan büyük ekipler için en iyisi.", + "UnlimitedUsers": "Sınırsız kullanıcı", + "UnlimitedObjects": "Sınırsız Huly nesneleri", + "ChangePlan": "Plan değiştir", + "Subscribe": "Abone ol", + "Monthly": "Aylık", + "Active": "Aktif", + "NoActivePlan": "Etkin plan yok", + "SelectPlanToBegin": "Başlamak için aşağıdan bir plan seçin", + "SubscriptionEnds": "{date} tarihine kadar", + "SubscriptionRenews": "Sonraki yenileme: {date}", + "SubscriptionValidUntil": "{date} tarihine kadar geçerli", + "ProcessingPayment": "Ödeme işleniyor...", + "Downgrade": "Düşür", + "CancelSubscription": "Aboneliği İptal Et", + "ConfirmUpgrade": "Aboneliği yükselt?", + "ConfirmDowngrade": "Aboneliği düşür?", + "ConfirmCancel": "Aboneliği iptal et?", + "UpgradeDescription": "Size hemen ${amount} kadar tahsil edilecektir. Tam tutar, mevcut faturalandırma döneminizie bağlıdır.", + "DowngradeDescription": "Kalan abonelik süresi için ${amount} kadar kredi alacaksınız. Düşürme hemen etkili olur.", + "CancelDescription": "Aboneliğiniz faturalandırma döneminin sonuna kadar etkin kalacaktır.", + "UncancelSubscription": "Aboneliği Yeniden Etkinleştir", + "ConfirmUncancel": "Aboneliği yeniden etkinleştir?", + "UncancelDescription": "Aboneliğiniz yeniden etkinleştirilecek ve geçerli faturalandırma döneminin sonundan itibaren devam edecektir.", + "PriceDifference": "Fiyat farkı: {amount}", + "DialogCancel": "İptal", + "DialogConfirm": "Onayla" } } diff --git a/plugins/billing-assets/lang/zh.json b/plugins/billing-assets/lang/zh.json index 8bdcdcda03..63e12584c3 100644 --- a/plugins/billing-assets/lang/zh.json +++ b/plugins/billing-assets/lang/zh.json @@ -1,6 +1,10 @@ { "string": { "Billing": "计费", + "Subscriptions": "订阅", + "AllPlans": "所有计划", + "ActivePlan": "当前计划", + "ResourceUsage": "资源使用情况", "DriveSize": "文件大小", "DriveCount": "文件数量", "OfficeSessionsDuration": "会议时间", @@ -8,6 +12,43 @@ "OfficeEgressDuration": "录制时间", "AI": "AI", "TranscriptionTime": "转录时间", - "TotalTokens": "总令牌数" + "TotalTokens": "总令牌数", + "Tier": "计划", + "StorageLimit": "{limit} {unit} 存储空间", + "TrafficLimit": "{limit} {unit} 视频/音频流量", + "Common": "Common", + "CommonDescription": "适合刚开始使用Huly的个人和团队。", + "Rare": "Rare", + "RareDescription": "适合个人创作者、自由职业者和微型机构。", + "Epic": "Epic", + "EpicDescription": "适合专业创意公司和小型企业。", + "Legendary": "Legendary", + "LegendaryDescription": "最适合需要最大功能的大型团队。", + "UnlimitedUsers": "无限用户", + "UnlimitedObjects": "无限Huly对象", + "ChangePlan": "更改计划", + "Subscribe": "订阅", + "Monthly": "每月", + "Active": "活跃", + "SubscriptionEnds": "截止至{date}", + "SubscriptionRenews": "下次续期: {date}", + "SubscriptionValidUntil": "有效期至{date}", + "ProcessingPayment": "正在处理付款...", + "Downgrade": "降级", + "CancelSubscription": "取消订阅", + "ConfirmUpgrade": "升级订阅?", + "ConfirmDowngrade": "降级订阅?", + "ConfirmCancel": "取消订阅?", + "UpgradeDescription": "将立即向您收取最多 ${amount}。具体金额取决于您当前的计费周期。", + "DowngradeDescription": "您将获得最多 ${amount} 的剩余订阅时间积分。降级立即生效。", + "CancelDescription": "您的订阅将在计费周期结束前保持活跃。", + "UncancelSubscription": "重新激活订阅", + "ConfirmUncancel": "重新激活订阅?", + "UncancelDescription": "您的订阅将被重新激活,并从当前计费周期结束时继续。", + "PriceDifference": "价格差异: {amount}", + "DialogCancel": "取消", + "DialogConfirm": "确认", + "NoActivePlan": "无活跃计划", + "SelectPlanToBegin": "选择下面的计划开始使用" } } diff --git a/plugins/billing-assets/src/index.ts b/plugins/billing-assets/src/index.ts index f1bf497852..158a2b265e 100644 --- a/plugins/billing-assets/src/index.ts +++ b/plugins/billing-assets/src/index.ts @@ -17,5 +17,6 @@ import billingPlugin from '@hcengineering/billing' const icons = require('../assets/icons.svg') as string // eslint-disable-line loadMetadata(billingPlugin.icon, { - Billing: `${icons}#billing` + Billing: `${icons}#billing`, + Subscriptions: `${icons}#subscriptions` }) diff --git a/plugins/billing-resources/package.json b/plugins/billing-resources/package.json index 48c468c685..22f896e23c 100644 --- a/plugins/billing-resources/package.json +++ b/plugins/billing-resources/package.json @@ -42,6 +42,7 @@ "@hcengineering/platform": "^0.7.17", "@hcengineering/presentation": "^0.7.0", "@hcengineering/theme": "^0.7.0", + "@hcengineering/core": "^0.7.18", "@hcengineering/ui": "^0.7.0", "@hcengineering/billing": "^0.7.0", "@hcengineering/view": "^0.7.0", @@ -49,6 +50,9 @@ "@hcengineering/billing-client": "^0.7.0", "@hcengineering/drive": "^0.7.0", "@hcengineering/love": "^0.7.0", + "@hcengineering/login": "^0.7.0", + "@hcengineering/account-client": "^0.7.18", + "@hcengineering/payment-client": "^0.7.0", "filesize": "^8.0.3", "svelte": "^4.2.20" } diff --git a/plugins/billing-resources/src/components/ResourceUsage.svelte b/plugins/billing-resources/src/components/ResourceUsage.svelte new file mode 100644 index 0000000000..ffd14a4663 --- /dev/null +++ b/plugins/billing-resources/src/components/ResourceUsage.svelte @@ -0,0 +1,139 @@ + + + +{#await loadBillingData()} + +{:then _} + +
+ +
+ + +
+
+ +
+ + +
+
+ +
+ + + +
+
+ formatDuration(v, $themeStore.language)} + data={sessionsDurationByDay} + /> +
+
+ Promise.resolve(filesize(v, { spacer: ' ' }))} + data={sessionsBandwidthByDay} + /> +
+
+ formatDuration(v, $themeStore.language)} + data={egressDurationByDay} + /> +
+
+
+
+{/await} + + diff --git a/plugins/billing-resources/src/components/Settings.svelte b/plugins/billing-resources/src/components/Settings.svelte index 84c0130ee2..6f1e477317 100644 --- a/plugins/billing-resources/src/components/Settings.svelte +++ b/plugins/billing-resources/src/components/Settings.svelte @@ -13,143 +13,104 @@ // limitations under the License. -->
- +
-
- {#await loadBillingData()} - - {:then _} - -
- -
- - -
-
- -
- - -
-
- -
- - - -
-
- formatDuration(v, $themeStore.language)} - data={sessionsDurationByDay} - /> -
-
- Promise.resolve(filesize(v, { spacer: ' ' }))} - data={sessionsBandwidthByDay} - /> -
-
- formatDuration(v, $themeStore.language)} - data={egressDurationByDay} - /> -
-
-
+
+ + + + +
+ +
- - diff --git a/plugins/billing-resources/src/components/Subscriptions.svelte b/plugins/billing-resources/src/components/Subscriptions.svelte new file mode 100644 index 0000000000..862d3cd56c --- /dev/null +++ b/plugins/billing-resources/src/components/Subscriptions.svelte @@ -0,0 +1,603 @@ + + + +{#if tiers.length > 0} + +
+
+
+
+
+ {#if loading || isCheckoutPolling} + + {#if isCheckoutPolling} +
+ {/if} + {:else if currentTier === undefined} +
+
+
+
+ {:else} +
+
+
+ {#if currentSubscription?.status === 'active'} +
+ {/if} +
+ {#if currentSubscription?.amount} +
+ + ${currentSubscription?.amount / 100} + + + +
+ {/if} +
+ + + {/if} +
+
+ +
+
+ +
+ {#each tiers as tier} + {@const color = + tier.color !== null && tier.color !== undefined && tier.color.length > 0 + ? getPlatformColorByName(tier.color, $themeStore.dark) + : null} + {@const bgAttr = $themeStore.dark ? 'background' : 'background-color'} +
+
+
+
+
+ + ${tier.priceMonthly} + + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ {/each} +
+
+
+
+
+{/if} + + diff --git a/plugins/billing-resources/src/index.ts b/plugins/billing-resources/src/index.ts index 3a9d05b775..e125d4c33d 100644 --- a/plugins/billing-resources/src/index.ts +++ b/plugins/billing-resources/src/index.ts @@ -1,8 +1,21 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + import type { Resources } from '@hcengineering/platform' import Settings from './components/Settings.svelte' -// export * from './utils' - export default async (): Promise => ({ component: { Settings diff --git a/plugins/billing-resources/src/plugin.ts b/plugins/billing-resources/src/plugin.ts new file mode 100644 index 0000000000..f296fdc202 --- /dev/null +++ b/plugins/billing-resources/src/plugin.ts @@ -0,0 +1,52 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import billing, { billingId } from '@hcengineering/billing' +import { type IntlString, mergeIds } from '@hcengineering/platform' + +export default mergeIds(billingId, billing, { + string: { + AllPlans: '' as IntlString, + ActivePlan: '' as IntlString, + ResourceUsage: '' as IntlString, + Subscriptions: '' as IntlString, + UnlimitedUsers: '' as IntlString, + UnlimitedObjects: '' as IntlString, + ChangePlan: '' as IntlString, + Subscribe: '' as IntlString, + Monthly: '' as IntlString, + Active: '' as IntlString, + NoActivePlan: '' as IntlString, + SelectPlanToBegin: '' as IntlString, + SubscriptionEnds: '' as IntlString, + SubscriptionRenews: '' as IntlString, + SubscriptionValidUntil: '' as IntlString, + ProcessingPayment: '' as IntlString, + Downgrade: '' as IntlString, + CancelSubscription: '' as IntlString, + ConfirmUpgrade: '' as IntlString, + ConfirmDowngrade: '' as IntlString, + ConfirmCancel: '' as IntlString, + UpgradeDescription: '' as IntlString, + DowngradeDescription: '' as IntlString, + CancelDescription: '' as IntlString, + UncancelSubscription: '' as IntlString, + ConfirmUncancel: '' as IntlString, + UncancelDescription: '' as IntlString, + PriceDifference: '' as IntlString, + DialogCancel: '' as IntlString, + DialogConfirm: '' as IntlString + } +}) diff --git a/plugins/billing-resources/src/utils.ts b/plugins/billing-resources/src/utils.ts index fd84d73c70..df5d9603ca 100644 --- a/plugins/billing-resources/src/utils.ts +++ b/plugins/billing-resources/src/utils.ts @@ -1,6 +1,7 @@ import { getMetadata } from '@hcengineering/platform' import presentation from '@hcengineering/presentation' import { getClient as getBillingClientRaw, type BillingClient } from '@hcengineering/billing-client' +import { getClient as getPaymentClientRaw, type PaymentClient } from '@hcengineering/payment-client' import billingPlugin from '@hcengineering/billing' export function getBillingClient (): BillingClient | null { @@ -11,3 +12,13 @@ export function getBillingClient (): BillingClient | null { const token = getMetadata(presentation.metadata.Token) return getBillingClientRaw(billingUrl, token) } + +export function getPaymentClient (): PaymentClient | null { + const paymentUrl = getMetadata(presentation.metadata.PaymentUrl) + if (paymentUrl === undefined || paymentUrl === '') { + return null + } + + const token = getMetadata(presentation.metadata.Token) + return getPaymentClientRaw(paymentUrl, token) +} diff --git a/plugins/billing/src/index.ts b/plugins/billing/src/index.ts index 7642eb8b5b..f4630649a5 100644 --- a/plugins/billing/src/index.ts +++ b/plugins/billing/src/index.ts @@ -14,5 +14,7 @@ // import { billingId, billingPlugin } from './plugin' +export * from './types' + export { billingId } export default billingPlugin diff --git a/plugins/billing/src/plugin.ts b/plugins/billing/src/plugin.ts index 59e7227d48..6509d28284 100644 --- a/plugins/billing/src/plugin.ts +++ b/plugins/billing/src/plugin.ts @@ -12,13 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import { Asset, type IntlString, type Metadata, plugin, type Plugin } from '@hcengineering/platform' +import { type Class, type Ref } from '@hcengineering/core' +import { type Asset, type IntlString, type Metadata, plugin, type Plugin } from '@hcengineering/platform' import { AnyComponent } from '@hcengineering/ui' +import { Tier } from './types' /** @public */ export const billingId = 'billing' as Plugin export const billingPlugin = plugin(billingId, { + class: { + Tier: '' as Ref> + }, metadata: { BillingURL: '' as Metadata }, @@ -34,10 +39,28 @@ export const billingPlugin = plugin(billingId, { OfficeEgressDuration: '' as IntlString, AI: '' as IntlString, TotalTokens: '' as IntlString, - TranscriptionTime: '' as IntlString + TranscriptionTime: '' as IntlString, + Tier: '' as IntlString, + StorageLimit: '' as IntlString, + TrafficLimit: '' as IntlString, + Common: '' as IntlString, + CommonDescription: '' as IntlString, + Rare: '' as IntlString, + RareDescription: '' as IntlString, + Epic: '' as IntlString, + EpicDescription: '' as IntlString, + Legendary: '' as IntlString, + LegendaryDescription: '' as IntlString }, icon: { - Billing: '' as Asset + Billing: '' as Asset, + Subscriptions: '' as Asset + }, + tier: { + Common: '' as Ref, + Rare: '' as Ref, + Epic: '' as Ref, + Legendary: '' as Ref } }) diff --git a/plugins/billing/src/types.ts b/plugins/billing/src/types.ts new file mode 100644 index 0000000000..bbaeb8f42d --- /dev/null +++ b/plugins/billing/src/types.ts @@ -0,0 +1,29 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Doc } from '@hcengineering/core' +import { IntlString } from '@hcengineering/platform' + +/** @public */ +export interface Tier extends Doc { + label: IntlString + description: IntlString + priceMonthly: number + storageLimitGB: number + trafficLimitGB: number + + index: number + color?: string +} diff --git a/plugins/calendar-resources/package.json b/plugins/calendar-resources/package.json index 3778d7e4ed..5c793c9b65 100644 --- a/plugins/calendar-resources/package.json +++ b/plugins/calendar-resources/package.json @@ -39,7 +39,7 @@ }, "dependencies": { "@hcengineering/analytics": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/login": "^0.7.0", "@hcengineering/core": "^0.7.18", "@hcengineering/platform": "^0.7.17", diff --git a/plugins/card-resources/package.json b/plugins/card-resources/package.json index d1d3476d47..c282f96885 100644 --- a/plugins/card-resources/package.json +++ b/plugins/card-resources/package.json @@ -72,7 +72,7 @@ "@hcengineering/card": "^0.7.0", "@hcengineering/communication": "^0.7.0", "@hcengineering/preference": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/chat": "^0.7.0", "fast-equals": "^5.2.2", "svelte": "^4.2.20" diff --git a/plugins/client-resources/package.json b/plugins/client-resources/package.json index 2aa14e3c14..74e90a872a 100644 --- a/plugins/client-resources/package.json +++ b/plugins/client-resources/package.json @@ -42,8 +42,8 @@ "@hcengineering/client": "^0.7.3", "@hcengineering/communication-sdk-types": "^0.7.5", "@hcengineering/communication-types": "^0.7.7", - "@hcengineering/core": "^0.7.10", - "@hcengineering/platform": "^0.7.5", + "@hcengineering/core": "^0.7.18", + "@hcengineering/platform": "^0.7.17", "@hcengineering/rpc": "^0.7.3", "snappyjs": "^0.7.0" }, diff --git a/plugins/client/package.json b/plugins/client/package.json index 439165d62e..8aac6e0091 100644 --- a/plugins/client/package.json +++ b/plugins/client/package.json @@ -37,8 +37,8 @@ "@types/jest": "^29.5.5" }, "dependencies": { - "@hcengineering/platform": "^0.7.5", - "@hcengineering/core": "^0.7.10" + "@hcengineering/platform": "^0.7.17", + "@hcengineering/core": "^0.7.18" }, "repository": "https://github.com/hcengineering/platform", "publishConfig": { diff --git a/plugins/contact-resources/package.json b/plugins/contact-resources/package.json index a405cdca42..fea76f6c07 100644 --- a/plugins/contact-resources/package.json +++ b/plugins/contact-resources/package.json @@ -61,7 +61,7 @@ "@hcengineering/view": "^0.7.0", "@hcengineering/view-resources": "^0.7.0", "@hcengineering/workbench": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/achievement": "^0.7.0", "svelte": "^4.2.20", "crypto-js": "^4.2.0", diff --git a/plugins/controlled-documents-resources/package.json b/plugins/controlled-documents-resources/package.json index 71d5f9d35a..61c3d95a37 100644 --- a/plugins/controlled-documents-resources/package.json +++ b/plugins/controlled-documents-resources/package.json @@ -50,7 +50,7 @@ "@hcengineering/view-resources": "^0.7.0", "@hcengineering/attachment": "^0.7.0", "@hcengineering/notification": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/contact": "^0.7.0", "@hcengineering/contact-resources": "^0.7.0", "@hcengineering/tags": "^0.7.0", diff --git a/plugins/global-profile-resources/package.json b/plugins/global-profile-resources/package.json index 2d553e5dca..958f00ca63 100644 --- a/plugins/global-profile-resources/package.json +++ b/plugins/global-profile-resources/package.json @@ -44,7 +44,7 @@ "@hcengineering/view": "^0.7.0", "@hcengineering/presentation": "^0.7.0", "@hcengineering/global-profile": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/login": "^0.7.0", "@hcengineering/contact": "^0.7.0", "@hcengineering/theme": "^0.7.0" diff --git a/plugins/gmail-resources/package.json b/plugins/gmail-resources/package.json index e87f8fa907..9aca2cacbf 100644 --- a/plugins/gmail-resources/package.json +++ b/plugins/gmail-resources/package.json @@ -62,7 +62,7 @@ "@hcengineering/analytics": "^0.7.17", "@hcengineering/templates": "^0.7.0", "@hcengineering/integration-client": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/setting-resources": "^0.7.0" } } diff --git a/plugins/guest-resources/package.json b/plugins/guest-resources/package.json index bf9b8c31a3..d1fc857313 100644 --- a/plugins/guest-resources/package.json +++ b/plugins/guest-resources/package.json @@ -48,7 +48,7 @@ "@hcengineering/presentation": "^0.7.0", "@hcengineering/guest": "^0.7.0", "@hcengineering/login": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/view-resources": "^0.7.0", "@hcengineering/analytics": "^0.7.17", "fast-copy": "^3.0.2", diff --git a/plugins/huly-mail-resources/package.json b/plugins/huly-mail-resources/package.json index 3f98821a00..34d38a98af 100644 --- a/plugins/huly-mail-resources/package.json +++ b/plugins/huly-mail-resources/package.json @@ -52,7 +52,7 @@ "@hcengineering/panel": "^0.7.0", "@hcengineering/analytics": "^0.7.17", "@hcengineering/integration-client": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/setting-resources": "^0.7.0", "@hcengineering/view-resources": "^0.7.0" } diff --git a/plugins/login-resources/package.json b/plugins/login-resources/package.json index f652b92ae4..0f670d3f1f 100644 --- a/plugins/login-resources/package.json +++ b/plugins/login-resources/package.json @@ -49,7 +49,7 @@ "@hcengineering/setting": "^0.7.0", "@hcengineering/theme": "^0.7.0", "@hcengineering/analytics": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/analytics-providers": "^0.7.0" } } diff --git a/plugins/login/package.json b/plugins/login/package.json index c8f2698c3c..0e8d21ff35 100644 --- a/plugins/login/package.json +++ b/plugins/login/package.json @@ -35,7 +35,7 @@ "jest": "^29.7.0", "ts-jest": "^29.1.1", "@types/jest": "^29.5.5", - "@hcengineering/account-client": "^0.7.17" + "@hcengineering/account-client": "^0.7.18" }, "dependencies": { "@hcengineering/core": "^0.7.18", diff --git a/plugins/love-resources/package.json b/plugins/love-resources/package.json index 5947ef14a8..d0ad4604e4 100644 --- a/plugins/love-resources/package.json +++ b/plugins/love-resources/package.json @@ -64,7 +64,7 @@ "@hcengineering/emoji": "^0.7.0", "@hcengineering/emoji-resources": "^0.7.0", "@hcengineering/theme": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/hulypulse-client": "^0.7.0", "@livekit/krisp-noise-filter": "^0.3.0", "@livekit/track-processors": "^0.5.6", diff --git a/plugins/setting-resources/package.json b/plugins/setting-resources/package.json index 55f394eb38..75305b15e6 100644 --- a/plugins/setting-resources/package.json +++ b/plugins/setting-resources/package.json @@ -42,7 +42,7 @@ "@hcengineering/analytics": "^0.7.17", "@hcengineering/core": "^0.7.18", "svelte": "^4.2.20", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/setting": "^0.7.0", "@hcengineering/contact": "^0.7.0", "@hcengineering/theme": "^0.7.0", diff --git a/plugins/setting/package.json b/plugins/setting/package.json index 36a857c20b..4145aa1594 100644 --- a/plugins/setting/package.json +++ b/plugins/setting/package.json @@ -41,7 +41,7 @@ "@hcengineering/core": "^0.7.18", "@hcengineering/templates": "^0.7.0", "@hcengineering/ui": "^0.7.0", - "@hcengineering/account-client": "^0.7.17" + "@hcengineering/account-client": "^0.7.18" }, "repository": "https://github.com/hcengineering/platform", "publishConfig": { diff --git a/plugins/telegram-resources/package.json b/plugins/telegram-resources/package.json index 12bfaf3dae..da38898075 100644 --- a/plugins/telegram-resources/package.json +++ b/plugins/telegram-resources/package.json @@ -38,7 +38,7 @@ "svelte-eslint-parser": "^0.33.1" }, "dependencies": { - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/analytics": "^0.7.17", "@hcengineering/attachment": "^0.7.0", "@hcengineering/attachment-resources": "^0.7.0", diff --git a/plugins/workbench-resources/package.json b/plugins/workbench-resources/package.json index d18af7cd34..24f73e88a2 100644 --- a/plugins/workbench-resources/package.json +++ b/plugins/workbench-resources/package.json @@ -61,7 +61,7 @@ "@hcengineering/communication-types": "^0.7.12", "fast-copy": "^3.0.2", "@hcengineering/analytics": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/chat": "^0.7.0", "@hcengineering/communication": "^0.7.0", "@hcengineering/rating": "^0.7.0", diff --git a/pods/authProviders/package.json b/pods/authProviders/package.json index c9438b8913..a16e6259f8 100644 --- a/pods/authProviders/package.json +++ b/pods/authProviders/package.json @@ -48,7 +48,7 @@ "mongodb": "^6.16.0", "@hcengineering/core": "^0.7.18", "@hcengineering/account": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "passport-custom": "~1.1.1", "passport-google-oauth20": "~2.0.0", "passport-github2": "~0.1.12", diff --git a/pods/media/package.json b/pods/media/package.json index ce8b11505c..99d449edc2 100644 --- a/pods/media/package.json +++ b/pods/media/package.json @@ -51,7 +51,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/analytics-service": "^0.7.17", "@hcengineering/analytics": "^0.7.17", "@hcengineering/core": "^0.7.18", diff --git a/pods/server/package.json b/pods/server/package.json index dc61c5cada..e76317dd02 100644 --- a/pods/server/package.json +++ b/pods/server/package.json @@ -91,7 +91,7 @@ "msgpackr-extract": "^3.0.3", "snappy": "^7.2.2", "ws": "^8.18.2", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "morgan": "^1.10.0", "body-parser": "^1.20.3", "cors": "^2.8.5", diff --git a/rush.json b/rush.json index 889370596c..2c28fe29b8 100644 --- a/rush.json +++ b/rush.json @@ -445,6 +445,11 @@ "projectFolder": "packages/billing-client", "shouldPublish": true }, + { + "packageName": "@hcengineering/payment-client", + "projectFolder": "packages/payment-client", + "shouldPublish": true + }, { "packageName": "@hcengineering/prod", "projectFolder": "dev/prod", @@ -1976,6 +1981,11 @@ "projectFolder": "services/billing/pod-billing", "shouldPublish": false }, + { + "packageName": "@hcengineering/pod-payment", + "projectFolder": "services/payment/pod-payment", + "shouldPublish": false + }, { "packageName": "@hcengineering/pod-love", "projectFolder": "services/love", diff --git a/server/account/src/__tests__/operations.test.ts b/server/account/src/__tests__/operations.test.ts index 441d1cab94..440285a180 100644 --- a/server/account/src/__tests__/operations.test.ts +++ b/server/account/src/__tests__/operations.test.ts @@ -48,7 +48,8 @@ import { changePassword, getPerson, getSocialIds, - createAccessLink + createAccessLink, + getSubscriptions } from '../operations' import { accountPlugin } from '../plugin' @@ -2580,3 +2581,151 @@ describe('account operations', () => { }) }) }) + +describe('getSubscriptions', () => { + const mockCtx = { + error: jest.fn(), + info: jest.fn() + } as unknown as MeasureContext + + const mockBranding = null + const workspaceUuid = 'test-workspace-uuid' as WorkspaceUuid + const accountUuid = 'test-account-uuid' as AccountUuid + + let mockDb: any + + beforeEach(() => { + jest.clearAllMocks() + + mockDb = { + subscription: { + find: jest.fn() + }, + getWorkspaceRole: jest.fn() + } + }) + + test('should return active subscriptions for workspace owner', async () => { + const mockSubscriptions = [ + { + id: 'sub-1', + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123', + type: 'tier', + status: 'active', + plan: 'pro' + } + ] + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: {} + }) + + mockDb.getWorkspaceRole.mockResolvedValue(AccountRole.Owner) + mockDb.subscription.find.mockResolvedValue(mockSubscriptions) + + const result = await getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', {}) + + expect(result).toEqual(mockSubscriptions) + expect(mockDb.subscription.find).toHaveBeenCalledWith({ workspaceUuid, status: 'active' }) + expect(mockDb.getWorkspaceRole).toHaveBeenCalledWith(accountUuid, workspaceUuid) + }) + + test('should return all subscriptions when activeOnly=false', async () => { + const mockSubscriptions = [ + { + id: 'sub-1', + status: 'active' + }, + { + id: 'sub-2', + status: 'canceled' + } + ] + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: {} + }) + + mockDb.getWorkspaceRole.mockResolvedValue(AccountRole.Owner) + mockDb.subscription.find.mockResolvedValue(mockSubscriptions) + + const result = await getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', { activeOnly: false }) + + expect(result).toEqual(mockSubscriptions) + expect(mockDb.subscription.find).toHaveBeenCalledWith({ workspaceUuid }) + }) + + test('should allow maintainer to view subscriptions', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: {} + }) + + mockDb.getWorkspaceRole.mockResolvedValue(AccountRole.Maintainer) + mockDb.subscription.find.mockResolvedValue([]) + + await getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', {}) + + expect(mockDb.getWorkspaceRole).toHaveBeenCalled() + expect(mockDb.subscription.find).toHaveBeenCalled() + }) + + test('should reject user without sufficient role', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: {} + }) + + mockDb.getWorkspaceRole.mockResolvedValue(AccountRole.User) + + await expect(getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', {})).rejects.toThrow(PlatformError) + }) + + test('should reject user without workspace membership', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: {} + }) + + mockDb.getWorkspaceRole.mockResolvedValue(null) + + await expect(getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', {})).rejects.toThrow(PlatformError) + }) + + test('should allow service to query any workspace', async () => { + const serviceWorkspaceUuid = 'different-workspace' as WorkspaceUuid + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: workspaceUuid, + extra: { service: 'billing' } + }) + + mockDb.subscription.find.mockResolvedValue([]) + + await getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', { workspaceUuid: serviceWorkspaceUuid }) + + expect(mockDb.subscription.find).toHaveBeenCalledWith({ workspaceUuid: serviceWorkspaceUuid, status: 'active' }) + expect(mockDb.getWorkspaceRole).not.toHaveBeenCalled() + }) + + test('should reject non-service users without workspace in token', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + account: accountUuid, + workspace: undefined, + extra: {} + }) + + await expect(getSubscriptions(mockCtx, mockDb, mockBranding, 'test-token', {})).rejects.toThrow(PlatformError) + }) +}) diff --git a/server/account/src/__tests__/serviceOperations.test.ts b/server/account/src/__tests__/serviceOperations.test.ts index b1f28d8de4..7d11125a32 100644 --- a/server/account/src/__tests__/serviceOperations.test.ts +++ b/server/account/src/__tests__/serviceOperations.test.ts @@ -14,6 +14,7 @@ // import { + type AccountUuid, type IntegrationKind, type MeasureContext, type PersonId, @@ -29,11 +30,12 @@ import { type Integration, type IntegrationKey, type IntegrationSecret, - type IntegrationSecretKey + type IntegrationSecretKey, + SubscriptionStatus, + SubscriptionType } from '../types' import * as utils from '../utils' import { - addIntegrationSecret, addSocialIdToPerson, createIntegration, deleteIntegration, @@ -43,7 +45,9 @@ import { listIntegrations, listIntegrationsSecrets, updateIntegration, - updateIntegrationSecret + updateIntegrationSecret, + addIntegrationSecret, + upsertSubscription } from '../serviceOperations' // Mock platform @@ -1403,3 +1407,199 @@ describe('integration methods', () => { }) }) }) + +describe('upsertSubscription', () => { + const mockCtx = { + error: jest.fn(), + info: jest.fn() + } as unknown as MeasureContext + + const mockBranding = null + const mockToken = 'test-token' + + let mockDb: any + let getWorkspaceByIdSpy: jest.SpyInstance + + beforeEach(() => { + jest.clearAllMocks() + + mockDb = { + subscription: { + findOne: jest.fn(), + insertOne: jest.fn(), + update: jest.fn() + } + } + + // Mock getWorkspaceById utility function + getWorkspaceByIdSpy = jest.spyOn(utils, 'getWorkspaceById') + }) + + afterAll(() => { + getWorkspaceByIdSpy.mockRestore() + }) + + test('should create new subscription', async () => { + const workspaceUuid = 'test-workspace' as WorkspaceUuid + const accountUuid = 'test-account' as AccountUuid + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + extra: { service: 'payment' } + }) + + getWorkspaceByIdSpy.mockResolvedValue({ uuid: workspaceUuid }) + mockDb.subscription.findOne.mockResolvedValue(null) + + const subscriptionData = { + id: 'sub-123', + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123', + providerCheckoutId: 'checkout-456', + type: SubscriptionType.Tier, + status: SubscriptionStatus.Active, + plan: 'pro' + } + + await upsertSubscription(mockCtx, mockDb, mockBranding, mockToken, subscriptionData) + + expect(getWorkspaceByIdSpy).toHaveBeenCalledWith(mockDb, workspaceUuid) + expect(mockDb.subscription.findOne).toHaveBeenCalledWith({ + provider: 'polar', + providerSubscriptionId: 'polar-sub-123' + }) + expect(mockDb.subscription.insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'sub-123', + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123', + status: 'active', + plan: 'pro' + }) + ) + }) + + test('should update existing subscription', async () => { + const workspaceUuid = 'test-workspace' as WorkspaceUuid + const accountUuid = 'test-account' as AccountUuid + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + extra: { service: 'payment' } + }) + + const existingSubscription = { + id: 'existing-sub-id', + workspaceUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123' + } + + getWorkspaceByIdSpy.mockResolvedValue({ uuid: workspaceUuid }) + mockDb.subscription.findOne.mockResolvedValue(existingSubscription) + + const subscriptionData = { + id: 'sub-123', + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123', + type: SubscriptionType.Tier, + status: SubscriptionStatus.Canceled, + plan: 'pro', + canceledAt: Date.now() + } + + await upsertSubscription(mockCtx, mockDb, mockBranding, mockToken, subscriptionData) + + expect(mockDb.subscription.update).toHaveBeenCalledWith( + { id: 'existing-sub-id' }, + expect.objectContaining({ + status: 'canceled', + canceledAt: subscriptionData.canceledAt + }) + ) + expect(mockDb.subscription.insertOne).not.toHaveBeenCalled() + }) + + test('should reject non-billing service', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + extra: { service: 'other-service' } + }) + + const subscriptionData = { + workspaceUuid: 'test-workspace' as WorkspaceUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123' + } as any + + await expect(upsertSubscription(mockCtx, mockDb, mockBranding, mockToken, subscriptionData)).rejects.toThrow( + new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + ) + + expect(mockDb.subscription.findOne).not.toHaveBeenCalled() + }) + + test('should reject if workspace not found', async () => { + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + extra: { service: 'billing' } + }) + + getWorkspaceByIdSpy.mockResolvedValue(null) + + const subscriptionData = { + workspaceUuid: 'nonexistent-workspace' as WorkspaceUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123' + } as any + + await expect(upsertSubscription(mockCtx, mockDb, mockBranding, mockToken, subscriptionData)).rejects.toThrow( + PlatformError + ) + + expect(mockDb.subscription.findOne).not.toHaveBeenCalled() + }) + + test('should handle subscription with optional fields', async () => { + const workspaceUuid = 'test-workspace' as WorkspaceUuid + const accountUuid = 'test-account' as AccountUuid + + ;(decodeTokenVerbose as jest.Mock).mockReturnValue({ + extra: { service: 'payment' } + }) + + getWorkspaceByIdSpy.mockResolvedValue({ uuid: workspaceUuid }) + mockDb.subscription.findOne.mockResolvedValue(null) + + const subscriptionData = { + id: 'sub-123', + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: 'polar-sub-123', + providerCheckoutId: 'checkout-456', + type: SubscriptionType.Tier, + status: SubscriptionStatus.Trialing, + plan: 'storage-100gb', + periodStart: Date.now(), + periodEnd: Date.now() + 30 * 24 * 60 * 60 * 1000, + trialEnd: Date.now() + 7 * 24 * 60 * 60 * 1000, + providerData: { + customerExternalId: 'cus_123', + metadata: { source: 'website' } + } + } + + await upsertSubscription(mockCtx, mockDb, mockBranding, mockToken, subscriptionData) + + expect(mockDb.subscription.insertOne).toHaveBeenCalledWith( + expect.objectContaining({ + providerCheckoutId: 'checkout-456', + trialEnd: subscriptionData.trialEnd, + providerData: subscriptionData.providerData + }) + ) + }) +}) diff --git a/server/account/src/collections/mongo.ts b/server/account/src/collections/mongo.ts index fc9298bcd9..9e8d8baeba 100644 --- a/server/account/src/collections/mongo.ts +++ b/server/account/src/collections/mongo.ts @@ -51,6 +51,7 @@ import type { SocialId, Sort, UserProfile, + Subscription, WorkspaceData, WorkspaceInfoWithStatus, WorkspaceInvite, @@ -405,6 +406,7 @@ export class MongoAccountDB implements AccountDB { integration: MongoDbCollection integrationSecret: MongoDbCollection userProfile: MongoDbCollection + subscription: MongoDbCollection workspaceMembers: MongoDbCollection @@ -423,6 +425,7 @@ export class MongoAccountDB implements AccountDB { this.integration = new MongoDbCollection('integration', db) this.integrationSecret = new MongoDbCollection('integrationSecret', db) this.userProfile = new MongoDbCollection('user_profile', db, 'personUuid') + this.subscription = new MongoDbCollection('subscription', db, 'id') this.workspaceMembers = new MongoDbCollection('workspaceMembers', db) } diff --git a/server/account/src/collections/postgres/migrations.ts b/server/account/src/collections/postgres/migrations.ts index f8396d62e9..d65c288932 100644 --- a/server/account/src/collections/postgres/migrations.ts +++ b/server/account/src/collections/postgres/migrations.ts @@ -36,7 +36,8 @@ export function getMigrations (ns: string): [string, string][] { getV15Migration(ns), getV16Migration(ns), getV17Migration(ns), - getV18Migration(ns) + getV18Migration(ns), + getV19Migration(ns) ] } @@ -490,3 +491,69 @@ function getV18Migration (ns: string): [string, string] { ` ] } + +function getV19Migration (ns: string): [string, string] { + return [ + 'account_db_v19_subscription_table', + ` + /* ======= S U B S C R I P T I O N ======= */ + /* Provider-agnostic subscription information for workspaces */ + /* Managed by billing service via payment provider webhooks (e.g. Polar.sh, Stripe) */ + /* Multiple active subscriptions allowed per workspace (tier + addons + support) */ + /* Historical subscriptions preserved with status: canceled/expired */ + + CREATE TYPE IF NOT EXISTS ${ns}.subscription_status AS ENUM ( + 'active', + 'trialing', + 'past_due', + 'canceled', + 'paused', + 'expired' + ); + + CREATE TABLE IF NOT EXISTS ${ns}.subscription ( + id STRING NOT NULL DEFAULT gen_random_uuid()::STRING, + workspace_uuid UUID NOT NULL, + account_uuid UUID NOT NULL, -- Account that paid for the subscription + + -- Provider details + provider STRING NOT NULL, -- Payment provider identifier (e.g. 'polar', 'stripe', 'manual') + provider_subscription_id STRING NOT NULL, -- External subscription ID from the provider + provider_checkout_id STRING, -- External checkout/session ID that created this subscription + + -- Subscription classification + type STRING NOT NULL DEFAULT 'tier', -- tier, support, etc. + status ${ns}.subscription_status NOT NULL DEFAULT 'active', + plan STRING NOT NULL, -- Plan identifier (e.g. 'rare', 'epic', 'legendary', 'custom') + + -- Amount paid (in cents, e.g. 9999 = $99.99) + -- Used primarily for pay-what-you-want/donation subscriptions to track actual payment + amount INT8, + + -- Billing period (optional) + period_start BIGINT, + period_end BIGINT, + + -- Trial information (optional) + trial_end BIGINT, + + -- Cancellation info (optional) + canceled_at BIGINT, + will_cancel_at BIGINT, -- Scheduled cancellation date (cancel at period end) + + -- Provider-specific data (stored as JSONB for flexibility) + -- e.g. customerExternalId, metadata, etc. + provider_data JSONB, + + created_on BIGINT NOT NULL DEFAULT current_epoch_ms(), + updated_on BIGINT NOT NULL DEFAULT current_epoch_ms(), + + CONSTRAINT subscription_pk PRIMARY KEY (id), + CONSTRAINT subscription_provider_subscription_id_unique UNIQUE (provider, provider_subscription_id), + CONSTRAINT subscription_workspace_fk FOREIGN KEY (workspace_uuid) REFERENCES ${ns}.workspace(uuid), + CONSTRAINT subscription_account_fk FOREIGN KEY (account_uuid) REFERENCES ${ns}.account(uuid), + INDEX subscription_workspace_status_idx (workspace_uuid, status) + ); + ` + ] +} diff --git a/server/account/src/collections/postgres/postgres.ts b/server/account/src/collections/postgres/postgres.ts index edfe18343c..efb26b1ff8 100644 --- a/server/account/src/collections/postgres/postgres.ts +++ b/server/account/src/collections/postgres/postgres.ts @@ -47,7 +47,8 @@ import type { Integration, IntegrationSecret, AccountAggregatedInfo, - UserProfile + UserProfile, + Subscription } from '../../types' function toSnakeCase (str: string): string { @@ -371,7 +372,7 @@ implements DbCollection { const castType = this.fieldTypes[key] currIdx++ updateChunks.push(`"${snakeKey}" = ${formatVar(currIdx, castType)}`) - values.push(ops[key]) + values.push(convertKeysToSnakeCase(ops[key])) } } } @@ -520,6 +521,7 @@ export class PostgresAccountDB implements AccountDB { integration: PostgresDbCollection integrationSecret: PostgresDbCollection userProfile: PostgresDbCollection + subscription: PostgresDbCollection constructor ( readonly client: Sql, @@ -569,6 +571,12 @@ export class PostgresAccountDB implements AccountDB { idKey: 'personUuid', withRetryClient }) + this.subscription = new PostgresDbCollection('subscription', client, { + ns, + idKey: 'id', + timestampFields: ['periodStart', 'periodEnd', 'trialEnd', 'canceledAt', 'willCancelAt', 'createdOn', 'updatedOn'], + withRetryClient + }) } getWsMembersTableName (): string { diff --git a/server/account/src/operations.ts b/server/account/src/operations.ts index ba0e5821ef..8eff2c19ca 100644 --- a/server/account/src/operations.ts +++ b/server/account/src/operations.ts @@ -61,7 +61,10 @@ import { type LoginInfoRequest, type LoginInfoRequestData, type Account, - type PersonWithProfile + type PersonWithProfile, + type Subscription, + SubscriptionStatus, + type Query } from './types' import { addSocialIdBase, @@ -2595,6 +2598,125 @@ async function getUserProfile ( throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) } +/** + * Get subscriptions for a workspace + * - Regular users: Must be OWNER or MAINTAINER of the workspace (from token) + * - Services: Can query any workspace by workspaceUuid parameter + * By default returns only active subscriptions. Set activeOnly=false to include historical subscriptions. + * @public + */ +export async function getSubscriptions ( + ctx: MeasureContext, + db: AccountDB, + branding: Branding | null, + token: string, + params: { + workspaceUuid?: WorkspaceUuid // Optional: used by services only, undefined means all workspaces + activeOnly?: boolean // Optional: default true - only return active subscriptions + } +): Promise { + const { account, extra, workspace: tokenWorkspace } = decodeTokenVerbose(ctx, token) + const { workspaceUuid, activeOnly = true } = params + + let targetWorkspace: WorkspaceUuid | null + + // Check if this is a service token + const isService = extra?.service !== undefined + + if (isService) { + // Services can query any workspace/all workspaces + targetWorkspace = workspaceUuid ?? null + } else { + // Regular users: use workspace from token (ignores workspaceUuid param) + if (tokenWorkspace === undefined) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {})) + } + targetWorkspace = tokenWorkspace + + // Verify user has OWNER or MAINTAINER role + const role = await db.getWorkspaceRole(account, targetWorkspace) + if (role === null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + const rolePower = getRolePower(role) + const maintainerPower = getRolePower(AccountRole.Maintainer) + + if (rolePower < maintainerPower) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + } + + // Fetch subscriptions for workspace (tier + addons + support) + // By default return only active subscriptions, unless activeOnly=false + const query: Query = targetWorkspace != null ? { workspaceUuid: targetWorkspace } : {} + if (activeOnly) { + query.status = SubscriptionStatus.Active + } + + const subscriptions = await db.subscription.find(query) + return subscriptions +} + +/** + * Get a subscription by its internal ID + * - Services: Can query any subscription + * - Regular users: Can only query subscriptions from their workspace (from token) + * @public + */ +export async function getSubscriptionById ( + ctx: MeasureContext, + db: AccountDB, + branding: Branding | null, + token: string, + params: { + subscriptionId: string // Internal subscription ID (UUID) + } +): Promise { + const { account, extra, workspace: tokenWorkspace } = decodeTokenVerbose(ctx, token) + const { subscriptionId } = params + + // Check if this is a service token + const isService = extra?.service !== undefined + + // Fetch the subscription first + const subscription = await db.subscription.findOne({ id: subscriptionId }) + + if (subscription === null || subscription === undefined) { + return null + } + + if (isService) { + // Services can query any subscription by internal ID + return subscription + } + + // Regular users: can only query subscriptions from their workspace (from token) + if (tokenWorkspace === undefined) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {})) + } + + // Verify the subscription belongs to the user's workspace + if (subscription.workspaceUuid !== tokenWorkspace) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + // Verify user has OWNER or MAINTAINER role in the workspace + const role = await db.getWorkspaceRole(account, tokenWorkspace) + if (role === null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + const rolePower = getRolePower(role) + const maintainerPower = getRolePower(AccountRole.Maintainer) + + if (rolePower < maintainerPower) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + return subscription +} + export type AccountMethods = | AccountServiceMethods | 'login' @@ -2655,6 +2777,8 @@ export type AccountMethods = | 'mergeSpecifiedPersons' | 'setMyProfile' | 'getUserProfile' + | 'getSubscriptions' + | 'getSubscriptionById' /** * @public @@ -2704,6 +2828,8 @@ export function getMethods (hasSignUp: boolean = true): Partial { + const { extra } = decodeTokenVerbose(ctx, token) + + // Only payment service can upsert subscriptions + if (extra?.service !== 'payment') { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + const { workspaceUuid, provider, providerSubscriptionId } = params + + // Verify workspace exists + const workspace = await getWorkspaceById(db, workspaceUuid) + if (workspace === null) { + throw new PlatformError(new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspaceUuid })) + } + + // Check if subscription exists by provider + providerSubscriptionId (unique external ID) + const existing = await db.subscription.findOne({ provider, providerSubscriptionId }) + const updateData = { + workspaceUuid: params.workspaceUuid, + accountUuid: params.accountUuid, + provider: params.provider, + providerSubscriptionId: params.providerSubscriptionId, + providerCheckoutId: params.providerCheckoutId, + amount: params.amount, + type: params.type, + status: params.status, + plan: params.plan, + periodStart: params.periodStart, + periodEnd: params.periodEnd, + trialEnd: params.trialEnd, + canceledAt: params.canceledAt, + willCancelAt: params.willCancelAt, + providerData: params.providerData, + updatedOn: Date.now() + } + if (existing !== null) { + // Update existing subscription + await db.subscription.update({ id: existing.id }, updateData) + ctx.info('Subscription updated', { + id: existing.id, + workspaceUuid, + status: params.status, + type: params.type, + plan: params.plan + }) + } else { + // Create new subscription + await db.subscription.insertOne({ + ...updateData, + id: params.id, + createdOn: Date.now() + }) + ctx.info('Subscription created', { + id: params.id, + workspaceUuid, + status: params.status, + type: params.type, + plan: params.plan + }) + } +} + +export async function getSubscriptionByProviderId ( + ctx: MeasureContext, + db: AccountDB, + branding: Branding | null, + token: string, + params: { + provider: string + providerSubscriptionId: string + } +): Promise { + const { extra } = decodeTokenVerbose(ctx, token) + + // Only payment service can query subscriptions by provider ID + if (extra?.service !== 'payment') { + throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {})) + } + + const { provider, providerSubscriptionId } = params + + // Find subscription by provider and providerSubscriptionId (unique external ID) + const subscription = await db.subscription.findOne({ + provider, + providerSubscriptionId + }) + + return subscription ?? null +} + export type AccountServiceMethods = | 'getPendingWorkspace' | 'updateWorkspaceInfo' @@ -1012,6 +1117,8 @@ export type AccountServiceMethods = | 'findPersonBySocialKey' | 'listAccounts' | 'findFullSocialIds' + | 'getSubscriptionByProviderId' + | 'upsertSubscription' /** * @public @@ -1043,6 +1150,8 @@ export function getServiceMethods (): Partial +/** + * Workspace subscription status + * Provider-agnostic abstraction for billing state + */ +export enum SubscriptionStatus { + Active = 'active', // Subscription is active and in good standing + Trialing = 'trialing', // In trial period + PastDue = 'past_due', // Payment failed but still providing service + Canceled = 'canceled', // Subscription has been canceled + Paused = 'paused', // Subscription is paused + Expired = 'expired' // Subscription or trial has expired +} + +/** + * Subscription type/purpose + * Allows multiple active subscriptions per workspace for different purposes + */ +export enum SubscriptionType { + Tier = 'tier', // Main workspace tier (free, starter, pro, enterprise) + Support = 'support' // Voluntary support/donation subscription +} + +/** + * Workspace subscription information + * Provider-agnostic subscription data managed by billing service + * Multiple subscriptions can be active per workspace (tier + addons + support) + * Historical subscriptions are preserved with status: canceled/expired + */ +export interface Subscription { + id: string // Our internal unique subscription ID (UUID) + workspaceUuid: WorkspaceUuid + accountUuid: AccountUuid // Account that paid for the subscription + + // Provider details + provider: string // Payment provider identifier (e.g. 'polar', 'stripe', 'manual') + providerSubscriptionId: string // External subscription ID from the provider + providerCheckoutId?: string // External checkout/session ID that created this subscription + + // Subscription classification + type: SubscriptionType // What this subscription is for (tier, addon, support) + status: SubscriptionStatus // Current status + plan: string // Plan/product identifier (e.g. 'free', 'pro', 'storage-100gb', 'supporter') + + // Amount paid (in cents, e.g. 9999 = $99.99) + // Used primarily for pay-what-you-want/donation subscriptions to track actual payment + amount?: number + + // Billing period (optional - not set for free/manual plans) + periodStart?: Timestamp + periodEnd?: Timestamp + + // Trial information (optional) + trialEnd?: Timestamp + + // Cancellation info (optional) + canceledAt?: Timestamp + willCancelAt?: Timestamp // Scheduled cancellation date (cancel at period end) + + // Provider-specific data (stored as JSONB for flexibility) + // This allows billing service to store additional provider fields if needed + // e.g. customerExternalId, metadata, etc. Some providers (like Polar.sh) allow using + // our own customer ID and don't require tracking their external customer ID + providerData?: Record + + createdOn: Timestamp + updatedOn: Timestamp +} + +export type SubscriptionData = Omit + /* ========= S U P P L E M E N T A R Y ========= */ export interface WorkspaceInfoWithStatus extends Workspace { @@ -237,6 +307,7 @@ export interface AccountDB { integration: DbCollection integrationSecret: DbCollection userProfile: DbCollection + subscription: DbCollection init: () => Promise createWorkspace: (data: WorkspaceData, status: WorkspaceStatusData) => Promise diff --git a/server/collaborator/package.json b/server/collaborator/package.json index 3b1b275d9d..2b4b038d24 100644 --- a/server/collaborator/package.json +++ b/server/collaborator/package.json @@ -52,7 +52,7 @@ "@hcengineering/analytics": "^0.7.17", "@hcengineering/core": "^0.7.18", "@hcengineering/account": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/platform": "^0.7.17", "@hcengineering/server-client": "^0.7.15", "@hcengineering/server-storage": "^0.7.15", diff --git a/server/front/package.json b/server/front/package.json index 0024a3a3ce..c3cc979d8a 100644 --- a/server/front/package.json +++ b/server/front/package.json @@ -53,7 +53,7 @@ "express-static-gzip": "^2.2.0", "uuid": "^8.3.2", "cors": "^2.8.5", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/server-core": "^0.7.16", "@hcengineering/storage": "^0.7.17", "@hcengineering/server-storage": "^0.7.15", diff --git a/server/front/src/index.ts b/server/front/src/index.ts index fba18d622a..5edef6e5dd 100644 --- a/server/front/src/index.ts +++ b/server/front/src/index.ts @@ -276,6 +276,7 @@ export function start ( streamUrl?: string mailUrl?: string billingUrl?: string + paymentUrl?: string pulseUrl?: string hulylakeUrl?: string datalakeUrl?: string @@ -354,6 +355,7 @@ export function start ( HIDE_LOCAL_LOGIN: config.hideLocalLogin, MAIL_URL: config.mailUrl, BILLING_URL: config.billingUrl, + PAYMENT_URL: config.paymentUrl, PULSE_URL: config.pulseUrl, HULYLAKE_URL: config.hulylakeUrl, DATALAKE_URL: config.datalakeUrl, diff --git a/server/front/src/starter.ts b/server/front/src/starter.ts index dab2da7f36..47a289d880 100644 --- a/server/front/src/starter.ts +++ b/server/front/src/starter.ts @@ -119,6 +119,8 @@ export function startFront (ctx: MeasureContext, extraConfig?: Record (str !== undefined ? Number(str) : undefined) + +const config: Config = (() => { + const params: Partial = { + Port: parseNumber(process.env.PORT) ?? 4040, + Secret: process.env.SECRET, + AccountsUrl: process.env.ACCOUNTS_URL, + FrontUrl: process.env.FRONT_URL, + UseSandbox: process.env.USE_SANDBOX === 'true', + PolarAccessToken: process.env.POLAR_ACCESS_TOKEN, + PolarWebhookSecret: process.env.POLAR_WEBHOOK_SECRET, + PolarOrganizationId: process.env.POLAR_ORGANIZATION_ID, + PolarSubscriptionPlans: process.env.POLAR_SUBSCRIPTION_PLANS, + ReconciliationIntervalMinutes: parseNumber(process.env.RECONCILIATION_INTERVAL_MINUTES) + } + + const requiredKeys: Array = ['Port', 'Secret', 'AccountsUrl', 'FrontUrl'] + const missingEnv = requiredKeys.filter((key) => params[key] === undefined) + + if (missingEnv.length > 0) { + throw Error(`Missing config for attributes: ${missingEnv.join(', ')}`) + } + + return params as Config +})() + +export default config diff --git a/services/payment/pod-payment/src/factory.ts b/services/payment/pod-payment/src/factory.ts new file mode 100644 index 0000000000..436ac1ec02 --- /dev/null +++ b/services/payment/pod-payment/src/factory.ts @@ -0,0 +1,82 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { type AccountClient } from '@hcengineering/account-client' +import type { PaymentProvider } from './providers' +import { PolarProvider } from './providers/polar/provider' + +/** + * Static singleton factory for creating payment providers + * Can support multiple payment providers (Polar, Stripe, etc.) + */ +export class PaymentProviderFactory { + private static instance: PaymentProviderFactory + + private constructor () {} + + static getInstance (): PaymentProviderFactory { + if (this.instance === undefined) { + this.instance = new PaymentProviderFactory() + } + return this.instance + } + + /** + * Create and cache a payment provider + * Only one provider can be initialized per instance + * Returns the cached provider if already initialized + */ + create ( + type: string, + config: Record, + accountClient: AccountClient, + useSandbox = false + ): PaymentProvider | undefined { + switch (type) { + case 'polar': + return this.createPolarProvider(config, accountClient, useSandbox) + default: + return undefined + } + } + + private createPolarProvider ( + config: Record, + accountClient: AccountClient, + useSandbox = false + ): PaymentProvider { + if (config.accessToken === undefined) { + throw new Error('Polar provider requires accessToken in config') + } + if (config.webhookSecret === undefined) { + throw new Error('Polar provider requires webhookSecret in config') + } + if (config.subscriptionPlans === undefined) { + throw new Error('Polar provider requires subscriptionPlans in config') + } + if (config.frontUrl === undefined) { + throw new Error('Polar provider requires frontUrl in config') + } + + return new PolarProvider( + config.accessToken, + config.webhookSecret, + config.subscriptionPlans, + config.frontUrl, + accountClient, + useSandbox + ) + } +} diff --git a/services/payment/pod-payment/src/index.ts b/services/payment/pod-payment/src/index.ts new file mode 100644 index 0000000000..6d9fa6d7d4 --- /dev/null +++ b/services/payment/pod-payment/src/index.ts @@ -0,0 +1,20 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { config } from 'dotenv' +import { main } from './main' + +config() +void main() diff --git a/services/payment/pod-payment/src/main.ts b/services/payment/pod-payment/src/main.ts new file mode 100644 index 0000000000..0b7e33237a --- /dev/null +++ b/services/payment/pod-payment/src/main.ts @@ -0,0 +1,69 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { Analytics } from '@hcengineering/analytics' +import { configureAnalytics, createOpenTelemetryMetricsContext, SplitLogger } from '@hcengineering/analytics-service' +import { newMetrics } from '@hcengineering/core' +import { setMetadata } from '@hcengineering/platform' +import serverClient from '@hcengineering/server-client' +import { initStatisticsContext } from '@hcengineering/server-core' +import serverToken from '@hcengineering/server-token' +import { join } from 'path' +import config from './config' +import { createServer, listen } from './server' + +const setupMetadata = (): void => { + setMetadata(serverToken.metadata.Secret, config.Secret) + setMetadata(serverToken.metadata.Service, 'payment') + setMetadata(serverClient.metadata.Endpoint, config.AccountsUrl) +} + +export const main = async (): Promise => { + setupMetadata() + + configureAnalytics('payment', process.env.VERSION ?? '0.7.0') + Analytics.setTag('application', 'payment') + + const metricsContext = initStatisticsContext('payment', { + factory: () => + createOpenTelemetryMetricsContext( + 'payment', + {}, + {}, + newMetrics(), + new SplitLogger('payment', { + root: join(process.cwd(), 'logs'), + enableConsole: (process.env.ENABLE_CONSOLE ?? 'true') === 'true' + }) + ) + }) + + const { app, close } = await createServer(metricsContext, config) + const server = listen(app, config.Port) + + const shutdown = (): void => { + close() + server.close(() => process.exit()) + } + + process.on('SIGINT', shutdown) + process.on('SIGTERM', shutdown) + process.on('uncaughtException', (e) => { + console.error(e) + }) + process.on('unhandledRejection', (e) => { + console.error(e) + }) +} diff --git a/services/payment/pod-payment/src/middleware.ts b/services/payment/pod-payment/src/middleware.ts new file mode 100644 index 0000000000..5b2e2191f7 --- /dev/null +++ b/services/payment/pod-payment/src/middleware.ts @@ -0,0 +1,101 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { NextFunction, Request, Response } from 'express' +import { extractToken, getAccountClient } from '@hcengineering/server-client' +import { AccountRole, systemAccountUuid } from '@hcengineering/core' +import { Token } from '@hcengineering/server-token' +import type { LoginInfo, LoginInfoRequest, WorkspaceLoginInfo } from '@hcengineering/account-client' + +export interface RequestWithAuth extends Request { + token?: Token + loginInfo?: LoginInfo | WorkspaceLoginInfo | LoginInfoRequest +} + +export const withToken = (req: RequestWithAuth, res: Response, next: NextFunction): void => { + const token = extractToken(req.headers) + if (token === undefined || token == null) { + res.status(401).json({ message: 'Token error' }).end() + return + } + req.token = token + next() +} + +export const withAdmin = (req: RequestWithAuth, res: Response, next: NextFunction): void => { + if (req.token === undefined || req.token == null) { + res.status(401).json({ message: 'Token error' }).end() + return + } + if (req.token.account !== systemAccountUuid && req.token.extra?.admin !== 'true') { + res.status(401).json({ message: 'Admins only' }).end() + return + } + next() +} + +export const withLoginInfo = (req: RequestWithAuth, res: Response, next: NextFunction): void => { + void withLoginInfoAsync(req, res, next) +} + +const withLoginInfoAsync = async (req: RequestWithAuth, res: Response, next: NextFunction): Promise => { + if (req.token === undefined || req.token == null) { + res.status(401).json({ message: 'Token error' }).end() + return + } + + const accountClient = getAccountClient(req.headers.authorization?.split(' ')[1]) + const loginInfo = await accountClient.getLoginInfoByToken() + if (loginInfo == null) { + res.status(403).json({ message: 'Missing auth info' }).end() + return + } + + req.loginInfo = loginInfo + next() +} + +export const withOwner = (req: RequestWithAuth, res: Response, next: NextFunction): void => { + void withOwnerAsync(req, res, next) +} + +const withOwnerAsync = async (req: RequestWithAuth, res: Response, next: NextFunction): Promise => { + if (req.token === undefined || req.token == null) { + res.status(401).json({ message: 'Token error' }).end() + return + } + if (req.params.workspace != null && req.token.workspace !== req.params.workspace) { + res.status(401).json({ message: 'Workspace mismatch' }).end() + return + } + if (req.token.account !== systemAccountUuid && req.token.extra?.admin !== 'true') { + const accountClient = getAccountClient(req.headers.authorization?.split(' ')[1]) + const loginInfo = req.loginInfo ?? (await accountClient.getLoginInfoByToken()) + if (loginInfo == null) { + res.status(403).json({ message: 'Missing auth info' }).end() + return + } + if (!('role' in loginInfo)) { + res.status(401).json({ message: 'Missing workspace role' }).end() + return + } + if (loginInfo.role !== AccountRole.Owner) { + res.status(401).json({ message: 'Workspace owners only' }).end() + return + } + } + + next() +} diff --git a/services/payment/pod-payment/src/providers/index.ts b/services/payment/pod-payment/src/providers/index.ts new file mode 100644 index 0000000000..c3b5da3783 --- /dev/null +++ b/services/payment/pod-payment/src/providers/index.ts @@ -0,0 +1,130 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { Express } from 'express' +import type { MeasureContext, WorkspaceUuid } from '@hcengineering/core' +import { type SubscriptionType, type SubscriptionData } from '@hcengineering/account-client' + +/** + * Payment subscription plan configuration + * Defines what plans are available for a subscription type + * TODO: take from billing from model + */ +export interface SubscriptionPlan { + id: string // Plan identifier + name: string // Display name + description?: string + price: number // Price in cents + currency: string // e.g. 'usd' +} + +/** + * Subscription request from client + * Used for subscribing to a plan + */ +export interface SubscribeRequest { + type: SubscriptionType + plan: string + customerEmail?: string + customerName?: string +} + +/** + * Checkout response when a new subscription or plan upgrade requires a checkout + */ +export interface CheckoutResponse { + checkoutId: string + checkoutUrl: string +} + +/** + * Payment provider abstraction + * Core interface that all payment providers must implement + * Includes both subscription operations and webhook handling + */ +export interface PaymentProvider { + get providerName(): string + + createSubscription: ( + ctx: MeasureContext, + request: SubscribeRequest, + workspaceUuid: WorkspaceUuid, + workspaceUrl: string, + accountUuid: string + ) => Promise + + /** + * Get subscription details + */ + getSubscription: (ctx: MeasureContext, subscriptionId: string) => Promise + + /** + * Get subscription by checkout ID + * Used to poll for subscription creation after successful checkout + * Returns the subscription data if found, null otherwise + */ + getSubscriptionByCheckout: (ctx: MeasureContext, checkoutId: string) => Promise + + /** + * Cancel a subscription + * Accepts provider subscription ID + * Returns the canceled subscription data + */ + cancelSubscription: (ctx: MeasureContext, providerSubscriptionId: string) => Promise + + /** + * Uncancel a subscription (reactivate a previously canceled subscription) + * Accepts provider subscription ID + * Returns the reactivated subscription data + */ + uncancelSubscription: (ctx: MeasureContext, providerSubscriptionId: string) => Promise + + /** + * Update a subscription to a different plan + * Used for upgrades and downgrades + * For paid-to-paid upgrades: Returns SubscriptionData | null + * For free-to-paid upgrades: Returns CheckoutResponse (requires checkout) + * Handles immediate effective date with proration + */ + updateSubscriptionPlan: ( + ctx: MeasureContext, + subscriptionId: string, + newPlan: string, + workspaceUrl: string + ) => Promise + + /** + * Reconcile active subscriptions between provider and our database + * This is provider-specific logic and should be delegated to the provider. + * All data fetching, transformation, and database updates are handled internally. + */ + reconcileActiveSubscriptions: (ctx: MeasureContext, accountsUrl: string, serviceToken: string) => Promise + + /** + * Register provider-specific webhook endpoints + * Called during server initialization + */ + registerWebhookEndpoints: (app: Express, ctx: MeasureContext, accountsUrl: string, serviceToken: string) => void +} + +/** + * Factory for creating payment providers + */ +export interface PaymentProviderFactory { + /** + * Create a payment provider instance + */ + create: (type: string, config: Record) => PaymentProvider | undefined +} diff --git a/services/payment/pod-payment/src/providers/polar/client.ts b/services/payment/pod-payment/src/providers/polar/client.ts new file mode 100644 index 0000000000..fa7be9cbec --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/client.ts @@ -0,0 +1,152 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { MeasureContext } from '@hcengineering/core' +import { Polar } from '@polar-sh/sdk' +import type { Subscription } from '@polar-sh/sdk/models/components/subscription' +import type { Checkout } from '@polar-sh/sdk/models/components/checkout' +import { SubscriptionProrationBehavior } from '@polar-sh/sdk/models/components/subscriptionprorationbehavior.js' +import type { CheckoutResult, CreateCheckoutParams } from './types' + +/** + * Documentation: https://docs.polar.sh/api-reference + */ +export class PolarClient { + private readonly polar: Polar + + constructor (accessToken: string, useSandbox = false) { + this.polar = new Polar({ + server: useSandbox ? 'sandbox' : undefined, + accessToken + }) + } + + /** + * Create a checkout session for product(s) + */ + async createCheckout (ctx: MeasureContext, params: CreateCheckoutParams): Promise { + return await ctx.with('polar-create-checkout', {}, async () => { + const checkout = await this.polar.checkouts.create({ + products: params.productIds, + successUrl: params.successUrl, + returnUrl: params.returnUrl, + subscriptionId: params.subscriptionId, + externalCustomerId: params.externalCustomerId, + customerEmail: params.customerEmail, + customerName: params.customerName, + metadata: params.metadata + }) + + return { + checkoutId: checkout.id, + url: checkout.url + } + }) + } + + /** + * Get checkout session by ID + */ + async getCheckout (ctx: MeasureContext, checkoutId: string): Promise { + return await ctx.with('polar-get-checkout', {}, async () => { + return await this.polar.checkouts.get({ id: checkoutId }) + }) + } + + /** + * Get subscription by ID + */ + async getSubscription (ctx: MeasureContext, subscriptionId: string): Promise { + return await ctx.with('polar-get-subscription', {}, async () => { + return await this.polar.subscriptions.get({ id: subscriptionId }) + }) + } + + /** + * Get all active subscriptions + * Filters subscriptions by active=true to get only active ones + * Filters by externalCustomerId if provided + * This is used internally by the provider for reconciliation + */ + async getActiveSubscriptions (ctx: MeasureContext, externalCustomerId?: string): Promise { + return await ctx.with('polar-get-active-subscriptions', {}, async () => { + const subscriptions: Subscription[] = [] + + const iterator = await this.polar.subscriptions.list({ + limit: 100, + active: true, + ...(externalCustomerId != null ? { externalCustomerId } : {}) + }) + + for await (const subscription of iterator) { + subscriptions.push(...subscription.result.items) + } + + return subscriptions + }) + } + + /** + * Cancel a subscription + * Polar subscriptions are canceled via update endpoint with cancelAtPeriodEnd flag + * Documentation: https://polar.sh/docs/api-reference/subscriptions/update#subscriptioncancel + */ + async cancelSubscription (ctx: MeasureContext, subscriptionId: string): Promise { + return await ctx.with('polar-cancel-subscription', {}, async () => { + const update = { + id: subscriptionId, + subscriptionUpdate: { + cancelAtPeriodEnd: true + } + } + return await this.polar.subscriptions.update(update) + }) + } + + /** + * Uncancel a subscription (reactivate a previously canceled subscription) + * Polar subscriptions are uncanceled via update endpoint with cancelAtPeriodEnd flag set to false + * Documentation: https://polar.sh/docs/api-reference/subscriptions/update#subscriptioncancel + */ + async uncancelSubscription (ctx: MeasureContext, subscriptionId: string): Promise { + return await ctx.with('polar-uncancel-subscription', {}, async () => { + const update = { + id: subscriptionId, + subscriptionUpdate: { + cancelAtPeriodEnd: false + } + } + return await this.polar.subscriptions.update(update) + }) + } + + /** + * Update a subscription to a different product + * Changes the subscription to use a new product with immediate effective date and proration + * Documentation: https://polar.sh/docs/api-reference/subscriptions/update + */ + async updateSubscription (ctx: MeasureContext, subscriptionId: string, productId: string): Promise { + return await ctx.with('polar-update-subscription', {}, async () => { + const update = { + id: subscriptionId, + subscriptionUpdate: { + productId, + prorationBehavior: SubscriptionProrationBehavior.Invoice + } + } + return await this.polar.subscriptions.update(update) + }) + } +} diff --git a/services/payment/pod-payment/src/providers/polar/index.ts b/services/payment/pod-payment/src/providers/polar/index.ts new file mode 100644 index 0000000000..d55f5dcf10 --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/index.ts @@ -0,0 +1,19 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +export * from './types' +export * from './client' +export * from './webhook' +export { PolarProvider } from './provider' diff --git a/services/payment/pod-payment/src/providers/polar/provider.ts b/services/payment/pod-payment/src/providers/polar/provider.ts new file mode 100644 index 0000000000..89a77c17bb --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/provider.ts @@ -0,0 +1,342 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { MeasureContext, WorkspaceUuid } from '@hcengineering/core' +import type { Express, Request, Response } from 'express' + +import { + AccountClient, + SubscriptionType, + type Subscription, + type SubscriptionData +} from '@hcengineering/account-client' +import type { PaymentProvider, SubscribeRequest, CheckoutResponse } from '../index' +import { PolarClient } from './client' +import { handlePolarWebhook } from './webhook' +import { transformPolarSubscriptionToData } from './utils' +import { getPlanKey } from '../../utils' + +/** + * Check if a subscription has changed by comparing modifiedAt timestamps + * Returns true if the provider's version is newer than what we have stored + */ +function hasSubscriptionChanged (ourSub: SubscriptionData, newData: SubscriptionData): boolean { + const ourModifiedAt = ourSub.providerData?.modifiedAt + const newModifiedAt = newData.providerData?.modifiedAt + + if (newModifiedAt === undefined) { + return false + } + + if (ourModifiedAt === undefined) { + return true + } + + return newModifiedAt > ourModifiedAt +} + +/** + * Polar.sh implementation of PaymentProvider + */ +export class PolarProvider implements PaymentProvider { + readonly providerName = 'polar' + private readonly polar: PolarClient + private readonly webhookSecret: string + // Map: plan@type (Huly) -> productIds (Polar) + private readonly subscriptionPlans: Record + private readonly frontUrl: string + private readonly accountClient: AccountClient + + constructor ( + accessToken: string, + webhookSecret: string, + subscriptionPlans: string, + frontUrl: string, + accountClient: AccountClient, + useSandbox = false + ) { + this.polar = new PolarClient(accessToken, useSandbox) + this.webhookSecret = webhookSecret + // TODO: support branding + this.frontUrl = frontUrl + this.subscriptionPlans = {} + this.accountClient = accountClient + const plans = subscriptionPlans.split(';') + for (const plan of plans) { + const [type, rawProductIds] = plan.split(':') + const productIds = rawProductIds.split(',') + this.subscriptionPlans[type] = productIds + } + // TODO: verify all plans are present in the config - take them from model? + // hardcoded check for now + const mustHave = ['common@tier', 'rare@tier', 'epic@tier', 'legendary@tier'] + for (const plan of mustHave) { + if (this.subscriptionPlans[plan] === undefined) { + throw new Error(`Missing plan in config: ${plan}`) + } + } + } + + async createSubscription ( + ctx: MeasureContext, + request: SubscribeRequest, + workspaceUuid: WorkspaceUuid, + workspaceUrl: string, + accountUuid: string + ): Promise { + ctx.info('Creating Polar subscription', { type: request.type, plan: request.plan }) + + const planKey = getPlanKey(request.type, request.plan) + const productIds = this.subscriptionPlans[planKey] + if (productIds === undefined) { + throw new Error(`Missing productIds for plan: ${planKey}`) + } + const successUrl = `${this.frontUrl}/workbench/${workspaceUrl}/setting/setting/billing/subscriptions?payment=success&checkout_id={CHECKOUT_ID}` + const returnUrl = `${this.frontUrl}/workbench/${workspaceUrl}/setting/setting/billing/subscriptions?payment=canceled` + const response = await this.polar.createCheckout(ctx, { + productIds, + successUrl, + returnUrl, + externalCustomerId: accountUuid, + customerEmail: request.customerEmail, + customerName: request.customerName, + metadata: { + workspaceUuid, + subscriptionType: request.type, + subscriptionPlan: request.plan + } + }) + + return { + checkoutId: response.checkoutId, + checkoutUrl: response.url + } + } + + async getSubscription (ctx: MeasureContext, subscriptionId: string): Promise { + const polarSubscription = await this.polar.getSubscription(ctx, subscriptionId) + const subscriptionData = transformPolarSubscriptionToData(polarSubscription) + + if (subscriptionData === null) { + return null + } + + return subscriptionData + } + + async getSubscriptionByCheckout (ctx: MeasureContext, checkoutId: string): Promise { + try { + const checkout = await this.polar.getCheckout(ctx, checkoutId) + + // If checkout is not succeeded, no subscription yet + if (checkout.status !== 'succeeded') { + return null + } + + // If checkout has a subscription ID, fetch it directly + if (checkout.subscriptionId != null) { + const subscription = await this.polar.getSubscription(ctx, checkout.subscriptionId) + const subscriptionData = transformPolarSubscriptionToData(subscription) + + if (subscriptionData !== null) { + return subscriptionData + } + } + + const customerId = checkout.externalCustomerId + if (customerId === undefined || customerId === null) { + ctx.error('Cannot search subscriptions: no customer ID in checkout', { checkoutId }) + return null + } + + const activeSubscriptions = await this.polar.getActiveSubscriptions(ctx, customerId) + + for (const polarSub of activeSubscriptions) { + // Check if this subscription was created from this checkout + if (polarSub.checkoutId === checkoutId) { + const subscriptionData = transformPolarSubscriptionToData(polarSub) + + if (subscriptionData !== null) { + return subscriptionData + } + } + } + + return null + } catch (err) { + ctx.error('Failed to get subscription by checkout', { checkoutId, err }) + return null + } + } + + async reconcileActiveSubscriptions (ctx: MeasureContext): Promise { + try { + ctx.info('Starting Polar active subscription reconciliation') + + const polarActiveSubscriptions = await this.polar.getActiveSubscriptions(ctx) + const ourActiveSubscriptions = await this.accountClient.getSubscriptions() + + const ourSubsByProviderId = new Map( + ourActiveSubscriptions.map((sub: Subscription) => [sub.providerSubscriptionId, sub]) + ) + + const polarActiveIds = new Set(polarActiveSubscriptions.map((sub: Record) => sub.id)) + + // Step 1: Update subscriptions that exist in Polar and have changed + let upsertCount = 0 + for (const polarSub of polarActiveSubscriptions) { + try { + const subscriptionData = transformPolarSubscriptionToData(polarSub) + if (subscriptionData === null) { + continue + } + + const ourSub = ourSubsByProviderId.get(polarSub.id) + + // Only upsert if subscription doesn't exist locally or if key fields have changed + if (ourSub === undefined || hasSubscriptionChanged(ourSub, subscriptionData)) { + await this.accountClient.upsertSubscription(subscriptionData) + upsertCount++ + } + } catch (err) { + ctx.error('Failed to upsert active subscription', { + providerSubId: polarSub.id, + err + }) + } + } + + // Step 2: Check for subscriptions we think are active but Polar says aren't + let staleCount = 0 + for (const ourSub of ourActiveSubscriptions) { + const polarSubId = ourSub.providerSubscriptionId + if (!polarActiveIds.has(polarSubId)) { + try { + // Fetch the current state from Polar directly + const currentState = await this.polar.getSubscription(ctx, polarSubId) + const subscriptionData = transformPolarSubscriptionToData(currentState) + + // Update our database with current state (may have changed to canceled/ended) + if (subscriptionData !== null) { + await this.accountClient.upsertSubscription(subscriptionData) + staleCount++ + } + } catch (err) { + ctx.error('Failed to reconcile subscription status', { + subscriptionId: polarSubId, + err + }) + } + } + } + + ctx.info('Polar subscription reconciliation completed', { + polarActiveCount: polarActiveSubscriptions.length, + ourActiveCount: ourActiveSubscriptions.length, + upsertedCount: upsertCount, + staleUpdatedCount: staleCount + }) + } catch (err) { + ctx.error('Polar subscription reconciliation failed', { err }) + throw err + } + } + + async cancelSubscription (ctx: MeasureContext, providerSubscriptionId: string): Promise { + const polarSubscription = await this.polar.cancelSubscription(ctx, providerSubscriptionId) + const subscriptionData = transformPolarSubscriptionToData(polarSubscription) + + if (subscriptionData == null) { + throw new Error(`Failed to cancel subscription ${providerSubscriptionId}`) + } + + return subscriptionData + } + + async uncancelSubscription (ctx: MeasureContext, providerSubscriptionId: string): Promise { + const polarSubscription = await this.polar.uncancelSubscription(ctx, providerSubscriptionId) + const subscriptionData = transformPolarSubscriptionToData(polarSubscription) + if (subscriptionData == null) { + throw new Error(`Failed to uncancel subscription ${providerSubscriptionId}`) + } + + return subscriptionData + } + + async updateSubscriptionPlan ( + ctx: MeasureContext, + subscriptionId: string, + newPlan: string, + workspaceUrl: string + ): Promise { + // Get the current subscription to check if it's free + const currentSub = await this.polar.getSubscription(ctx, subscriptionId) + + // Check if subscription is free by checking if it has a price with amountType === 'free' + const isFreeSubscription = currentSub.prices?.[0]?.amountType === 'free' + + // Get the Polar product ID for the new plan (subscriptions updates are tier type) + const planKey = getPlanKey(SubscriptionType.Tier, newPlan) + const productIds = this.subscriptionPlans[planKey] + if (productIds === undefined || productIds.length === 0) { + throw new Error(`No products configured for plan: ${planKey}`) + } + + // Use first product ID from the list (it should be the default fixed amount subscription plan) + const newProductId = productIds[0] + + // If subscription is free, create a checkout instead of updating directly + if (isFreeSubscription) { + const successUrl = `${this.frontUrl}/workbench/${workspaceUrl}/setting/setting/billing/subscriptions?payment=success&checkout_id={CHECKOUT_ID}` + const returnUrl = `${this.frontUrl}/workbench/${workspaceUrl}/setting/setting/billing/subscriptions?payment=canceled` + + const response = await this.polar.createCheckout(ctx, { + productIds: [newProductId], + successUrl, + returnUrl, + subscriptionId: currentSub.id, + externalCustomerId: currentSub.customerId, + customerEmail: currentSub.customer?.email ?? undefined, + customerName: currentSub.customer?.name ?? undefined, + metadata: { + workspaceUuid: (currentSub.metadata?.workspaceUuid as string) ?? '', + subscriptionType: SubscriptionType.Tier, + subscriptionPlan: newPlan + } + }) + + return { + checkoutId: response.checkoutId, + checkoutUrl: response.url + } + } + + // Update the subscription to the new product + const updatedSub = await this.polar.updateSubscription(ctx, subscriptionId, newProductId) + + // Transform and return the updated subscription data + const subscriptionData = transformPolarSubscriptionToData(updatedSub) + return subscriptionData + } + + registerWebhookEndpoints (app: Express, ctx: MeasureContext, accountsUrl: string, serviceToken: string): void { + ctx.info('Registering Polar webhook endpoints') + + // Register Polar-specific webhook endpoint (body parsing handled by server middleware) + app.post('/api/v1/webhooks/polar', (req: Request, res: Response) => { + void handlePolarWebhook(ctx, accountsUrl, serviceToken, this.webhookSecret, req, res) + }) + } +} diff --git a/services/payment/pod-payment/src/providers/polar/types.ts b/services/payment/pod-payment/src/providers/polar/types.ts new file mode 100644 index 0000000000..70f33e415e --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/types.ts @@ -0,0 +1,55 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/** + * Polar.sh integration types + * Uses @polar-sh/sdk for API interactions + * Documentation: https://docs.polar.sh/api-reference + */ +/** + * Webhook event wrapper + */ +export interface PolarWebhookEvent { + type: string + data: any +} + +export interface CreateCheckoutMetadata { + workspaceUuid: string + subscriptionType: string + subscriptionPlan: string + [key: string]: string | number | boolean +} + +/** + * Internal types for checkout creation + * Note: API uses 'products' parameter (array of product IDs) + */ +export interface CreateCheckoutParams { + productIds: string[] + successUrl: string + returnUrl?: string + externalCustomerId: string + subscriptionId?: string + metadata: CreateCheckoutMetadata + customerEmail?: string + customerName?: string + customerMetadata?: Record +} + +export interface CheckoutResult { + checkoutId: string + url: string +} diff --git a/services/payment/pod-payment/src/providers/polar/utils.ts b/services/payment/pod-payment/src/providers/polar/utils.ts new file mode 100644 index 0000000000..5ea7fcebdc --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/utils.ts @@ -0,0 +1,125 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// +import { type AccountUuid, type WorkspaceUuid } from '@hcengineering/core' +import type { SubscriptionData } from '@hcengineering/account-client' +import { SubscriptionStatus, SubscriptionType } from '@hcengineering/account-client' + +/** + * Map Polar subscription status to our SubscriptionStatus + */ +function mapPolarStatus (polarStatus: string): SubscriptionStatus | null { + switch (polarStatus) { + case 'active': + return 'active' as SubscriptionStatus + case 'trialing': + return 'trialing' as SubscriptionStatus + case 'past_due': + return 'past_due' as SubscriptionStatus + case 'canceled': + return 'canceled' as SubscriptionStatus + case 'unpaid': + case 'incomplete': + case 'incomplete_expired': + default: + return null + } +} + +/** + * Transform a Polar subscription to our SubscriptionData format + * Extracts metadata and maps status + * Returns null if subscription is in an irrelevant state + */ +export function transformPolarSubscriptionToData (subscription: any): SubscriptionData | null { + const metadata = subscription.metadata ?? {} + const workspaceUuid = metadata.workspaceUuid as WorkspaceUuid | undefined + const accountUuid = subscription.customer?.externalId as AccountUuid | undefined + const subscriptionType = metadata.subscriptionType as SubscriptionType | undefined + const subscriptionPlan = metadata.subscriptionPlan as string | undefined + + if ( + accountUuid === undefined || + workspaceUuid === undefined || + subscriptionType === undefined || + subscriptionPlan === undefined + ) { + return null + } + + // With supporter subscriptions of type pay-what-you-want the actual plan should be + // determined by the amount paid as it's not possible to define maximum sum for these products. + // E.g. one can pay 1000$ using common supporter product which will correspond to 'common' plan + // in metadata but should be treated like 'legendary' plan in actual subscription. + // Conditions are hardcoded for now. + // TODO: take from model + let actualPlan = subscriptionPlan + const amount = subscription.amount as number | undefined + if (amount !== undefined) { + if (amount < 1999) { + actualPlan = 'common' + } else if (amount >= 1999 && amount < 9999) { + actualPlan = 'rare' + } else if (amount >= 9999 && amount < 39999) { + actualPlan = 'epic' + } else if (amount >= 39999) { + actualPlan = 'legendary' + } + } + + // Map Polar status to our SubscriptionStatus + const status = mapPolarStatus(subscription.status) + + if (status === null) { + // Ignore updates for subscriptions in irrelevant states + return null + } + + // Handle optional periodEnd field - explicit null check + let periodEnd: number | undefined + if (subscription.currentPeriodEnd != null) { + periodEnd = new Date(subscription.currentPeriodEnd).getTime() + } + + const subscriptionData: SubscriptionData = { + id: `polar_${subscription.id}`, // Composite ID with provider prefix to avoid conflicts + workspaceUuid, + accountUuid, + provider: 'polar', + providerSubscriptionId: subscription.id, + providerCheckoutId: subscription.checkoutId, + type: subscriptionType, + status, + plan: actualPlan, + amount, // Amount paid in cents (e.g. 9999 for $99.99) - used for pay-what-you-want tracking + periodStart: new Date(subscription.currentPeriodStart).getTime(), + periodEnd, + trialEnd: subscription.trialEnd !== undefined ? new Date(subscription.trialEnd).getTime() : undefined, + canceledAt: subscription.canceledAt !== undefined ? new Date(subscription.canceledAt).getTime() : undefined, + providerData: { + modifiedAt: + subscription.modifiedAt !== null + ? new Date(subscription.modifiedAt).getTime() + : new Date(subscription.createdAt).getTime(), + customerId: subscription.customerId, + status: subscription.status, + cancelAtPeriodEnd: subscription.cancelAtPeriodEnd, + endedAt: subscription.endedAt, + cancelReason: subscription.customerCancellationReason, + cancelComment: subscription.customerCancellationComment + } + } + + return subscriptionData +} diff --git a/services/payment/pod-payment/src/providers/polar/webhook.ts b/services/payment/pod-payment/src/providers/polar/webhook.ts new file mode 100644 index 0000000000..fdca8999ee --- /dev/null +++ b/services/payment/pod-payment/src/providers/polar/webhook.ts @@ -0,0 +1,110 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import type { Request, Response } from 'express' +import { validateEvent, WebhookVerificationError } from '@polar-sh/sdk/webhooks' +import { type MeasureContext } from '@hcengineering/core' + +import { getAccountClient } from '../../utils' +import { transformPolarSubscriptionToData } from './utils' + +/** + * Handle Polar.sh webhook events + * Uses @polar-sh/sdk for webhook validation + * Webhooks are sent for checkout and subscription lifecycle events + * Documentation: https://polar.sh/docs/integrate/webhooks/delivery + */ +export async function handlePolarWebhook ( + ctx: MeasureContext, + accountsUrl: string, + serviceToken: string, + webhookSecret: string, + req: Request, + res: Response +): Promise { + try { + // Body is a Buffer from express.raw() middleware + const rawBody = req.body as Buffer + + if (!(rawBody instanceof Buffer) || rawBody.length === 0) { + ctx.error('Invalid webhook body') + res.status(400).json({ error: 'Invalid body' }) + return + } + + // Validate webhook signature and parse event + const event = validateEvent(rawBody, req.headers as Record, webhookSecret) + + // Route to appropriate handler based on event type + switch (event.type) { + case 'subscription.created': + case 'subscription.updated': + case 'subscription.active': + case 'subscription.canceled': + case 'subscription.uncanceled': + case 'subscription.revoked': + void handleSubscriptionUpdated(ctx, accountsUrl, serviceToken, event).catch((err) => { + ctx.error('Failed to process Polar webhook event', { event, err }) + }) + break + default: + } + + res.status(202).json({ received: true }) + } catch (err) { + // Check if it's a validation error by class name + const isValidationError = err instanceof WebhookVerificationError + + if (isValidationError) { + ctx.error('Invalid Polar webhook signature', { err }) + res.status(403).json({ error: 'Invalid signature' }) + return + } + + ctx.error('Failed to process Polar webhook', { err }) + res.status(500).json({ error: 'Internal server error' }) + } +} + +/** + * Handle subscription.created/updated/active + */ +async function handleSubscriptionUpdated ( + ctx: MeasureContext, + accountsUrl: string, + serviceToken: string, + event: any +): Promise { + const subscription = event.data ?? event + if (subscription == null) { + ctx.error('Missing subscription data', { event }) + throw new Error('Missing subscription data') + } + + const subscriptionData = transformPolarSubscriptionToData(subscription) + + if (subscriptionData === null) { + ctx.info('Ignoring subscription in irrelevant state', { + subscriptionId: subscription.id, + status: subscription.status + }) + return + } + + const accountClient = getAccountClient(accountsUrl, serviceToken) + await accountClient.upsertSubscription(subscriptionData) + + ctx.info('Subscription upserted', { subscriptionId: subscription.id, status: subscriptionData.status }) +} diff --git a/services/payment/pod-payment/src/reconciliation.ts b/services/payment/pod-payment/src/reconciliation.ts new file mode 100644 index 0000000000..925ce28ed8 --- /dev/null +++ b/services/payment/pod-payment/src/reconciliation.ts @@ -0,0 +1,55 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { MeasureContext } from '@hcengineering/core' +import type { PaymentProvider } from './providers' + +/** + * Active subscription reconciliation timer management + * + * Delegates the reconciliation logic to the payment provider. + * This ensures all provider-specific logic stays within the providers folder. + */ + +/** + * Start periodic active subscription reconciliation + * Delegates to provider's reconcileActiveSubscriptions method + */ +export function startActiveSubscriptionReconciliation ( + ctx: MeasureContext, + accountsUrl: string, + serviceToken: string, + provider: PaymentProvider, + intervalMinutes: number +): () => void { + // Run reconciliation immediately on start + void provider.reconcileActiveSubscriptions(ctx, accountsUrl, serviceToken).catch((err: any) => { + ctx.error('Initial subscription reconciliation failed', { provider: provider.providerName, err }) + }) + + // Then run periodically + const intervalMs = intervalMinutes * 60 * 1000 + const timer = setInterval(() => { + void provider.reconcileActiveSubscriptions(ctx, accountsUrl, serviceToken).catch((err: any) => { + ctx.error('Periodic subscription reconciliation failed', { provider: provider.providerName, err }) + }) + }, intervalMs) + + ctx.info('Active subscription reconciliation started', { provider: provider.providerName, intervalMinutes }) + + return () => { + clearInterval(timer) + } +} diff --git a/services/payment/pod-payment/src/server.ts b/services/payment/pod-payment/src/server.ts new file mode 100644 index 0000000000..b65751e5bf --- /dev/null +++ b/services/payment/pod-payment/src/server.ts @@ -0,0 +1,551 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import cors from 'cors' +import express, { type Express, NextFunction, type Request, type Response } from 'express' +import { type Server } from 'http' +import { MeasureContext, systemAccountUuid } from '@hcengineering/core' +import morgan from 'morgan' +import onHeaders from 'on-headers' +import rateLimit from 'express-rate-limit' +import { generateToken } from '@hcengineering/server-token' +import { SubscriptionData, type WorkspaceLoginInfo } from '@hcengineering/account-client' + +import { Config } from './config' +import { withAdmin, withLoginInfo, withOwner, withToken, type RequestWithAuth } from './middleware' +import { PaymentProviderFactory } from './factory' +import type { CheckoutResponse, PaymentProvider } from './providers' +import { SubscribeRequest } from './providers' +import { startActiveSubscriptionReconciliation } from './reconciliation' +import { getAccountClient } from './utils' + +const KEEP_ALIVE_TIMEOUT = 5 // seconds + +const subscriptionRateLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 5, // limit each IP to 5 requests per windowMs + message: 'Too many subscription requests, please try again later', + standardHeaders: true +}) + +type AsyncRequestHandler = (ctx: MeasureContext, req: Request, res: Response) => Promise + +const handleRequest = async ( + ctx: MeasureContext, + name: string, + fn: AsyncRequestHandler, + req: Request, + res: Response, + next: NextFunction +): Promise => { + try { + await ctx.with(name, {}, (ctx) => { + onHeaders(res, () => { + const measurements = ctx.metrics?.measurements + if (measurements !== undefined) { + const values = [] + for (const [k, v] of Object.entries(measurements)) { + values.push(`${k};dur=${v.value.toFixed(2)}`) + } + if (values.length > 0) { + if (!res.headersSent) { + res.setHeader('Server-Timing', values.join(', ')) + } + } + } + }) + return fn(ctx, req, res) + }) + } catch (err: unknown) { + ctx.error('Failed to process payment request', { err }) + res.status(500).end() + } +} + +export async function createServer (ctx: MeasureContext, config: Config): Promise<{ app: Express, close: () => void }> { + const app = express() + app.use(cors()) + + const childLogger = ctx.logger.childLogger?.('requests', { enableConsole: 'true' }) + const requests = ctx.newChild('requests', {}, { logger: childLogger, span: false }) + class LogStream { + write (text: string): void { + requests.info(text) + } + } + + app.use(morgan('short', { stream: new LogStream() })) + + // Apply JSON parsing conditionally - skip for webhook routes + app.use((req, res, next) => { + if (req.path.startsWith('/api/v1/webhooks/')) { + // Webhooks need raw body for signature verification + express.raw({ type: 'application/json' })(req, res, next) + } else { + // JSON parsing for all other routes + express.json()(req, res, next) + } + }) + + const serviceToken = generateToken(systemAccountUuid, undefined, { service: 'payment' }) + const accountClient = getAccountClient(config.AccountsUrl, serviceToken) + + // Initialize payment provider if configured + let provider: PaymentProvider | undefined + + if ( + config.PolarAccessToken !== undefined && + config.PolarWebhookSecret !== undefined && + config.PolarSubscriptionPlans !== undefined + ) { + try { + provider = PaymentProviderFactory.getInstance().create( + 'polar', + { + accessToken: config.PolarAccessToken, + webhookSecret: config.PolarWebhookSecret, + subscriptionPlans: config.PolarSubscriptionPlans, + frontUrl: config.FrontUrl + }, + accountClient, + config.UseSandbox + ) + + if (provider !== undefined) { + // Register provider-specific endpoints (e.g., webhooks) + provider.registerWebhookEndpoints(app, ctx, config.AccountsUrl, serviceToken) + + ctx.info('polar.sh payment provider initialized successfully') + } + } catch (err) { + ctx.error('Failed to initialize payment provider polar.sh', { err }) + } + } + + if (provider == null) { + throw new Error('Payment provider is not configured. Please provide payment provider configuration.') + } + + const stopReconciliation = startActiveSubscriptionReconciliation( + ctx, + config.AccountsUrl, + serviceToken, + provider, + config.ReconciliationIntervalMinutes ?? 60 + ) + + // ============ Generic Payment Service Endpoints ============ + // These endpoints are provider-agnostic and work with any payment provider + + /** + * POST /api/v1/subscriptions/:workspace/subscribe + * Create a subscription for a workspace + * Body: SubscribeRequest { type: 'tier' | 'support', plan: string, ... } + */ + app.post( + '/api/v1/subscriptions/:workspace/subscribe', + subscriptionRateLimiter, + withToken, + withLoginInfo, + withOwner, + (req: RequestWithAuth, res: Response) => { + if (provider === undefined || serviceToken === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + void handleRequest( + ctx, + 'create-subscription', + async (ctx) => { + const workspaceUuid = req.token?.workspace + const accountUuid = req.token?.account + const request = req.body as SubscribeRequest + const loginInfo = req.loginInfo as WorkspaceLoginInfo + + if (accountUuid === undefined) { + res.status(401).json({ error: 'Missing account in token' }) + return + } + + if (workspaceUuid === undefined) { + res.status(401).json({ error: 'Missing workspace in token' }) + return + } + + if (loginInfo?.workspaceUrl === undefined) { + res.status(401).json({ error: 'Missing workspace url in login info' }) + return + } + + if (request.type === undefined || request.plan === undefined) { + res.status(400).json({ error: 'Missing required fields: type, plan' }) + return + } + + let createSubResponse: CheckoutResponse + + try { + createSubResponse = await provider.createSubscription( + ctx, + request, + workspaceUuid, + loginInfo.workspaceUrl, + accountUuid + ) + } catch (err) { + ctx.error('Failed to create subscription at provider', { err }) + res.status(500).json({ error: 'Failed to create subscription at provider' }) + return + } + + res.status(200).json(createSubResponse) + }, + req, + res, + () => {} + ) + } + ) + + /** + * POST /api/v1/subscriptions/:subscriptionId/cancel + * Cancel a subscription + * Authorization: Only workspace owner/admin can cancel + */ + app.post( + '/api/v1/subscriptions/:subscriptionId/cancel', + withToken, + withOwner, + (req: RequestWithAuth, res: Response) => { + if (provider === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + void handleRequest( + ctx, + 'cancel-subscription', + async (ctx) => { + const subscriptionId = req.params.subscriptionId + + // Get subscription from our database using internal ID + const subscription = await accountClient.getSubscriptionById(subscriptionId) + + if (subscription === undefined || subscription === null) { + res.status(404).json({ error: 'Subscription not found' }) + return + } + + let canceledSubscription: SubscriptionData | null + + try { + // Cancel via provider using the provider's subscription ID + canceledSubscription = await provider.cancelSubscription(ctx, subscription.providerSubscriptionId) + } catch (err) { + ctx.error('Failed to cancel subscription at provider', { err }) + res.status(500).json({ error: 'Failed to cancel subscription at provider' }) + return + } + + if (canceledSubscription === null) { + res.status(404).json({ error: 'Failed to cancel subscription at provider' }) + return + } + + // Upsert the updated subscription into our database + await accountClient.upsertSubscription(canceledSubscription) + + res.status(200).json(canceledSubscription) + }, + req, + res, + () => {} + ) + } + ) + + /** + * POST /api/v1/subscriptions/:subscriptionId/uncancel + * Uncancel a previously canceled subscription + * Authorization: Only workspace owner/admin can uncancel + */ + app.post( + '/api/v1/subscriptions/:subscriptionId/uncancel', + withToken, + withOwner, + (req: RequestWithAuth, res: Response) => { + if (provider === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + void handleRequest( + ctx, + 'uncancel-subscription', + async (ctx) => { + const subscriptionId = req.params.subscriptionId + + // Get subscription from our database using internal ID + const subscription = await accountClient.getSubscriptionById(subscriptionId) + + if (subscription === undefined || subscription === null) { + res.status(404).json({ error: 'Subscription not found' }) + return + } + + let uncanceledSubscription: SubscriptionData | null + + try { + // Uncancel via provider using the provider's subscription ID + uncanceledSubscription = await provider.uncancelSubscription(ctx, subscription.providerSubscriptionId) + } catch (err) { + ctx.error('Failed to uncancel subscription at provider', { err }) + res.status(500).json({ error: 'Failed to uncancel subscription at provider' }) + return + } + + if (uncanceledSubscription === null) { + res.status(404).json({ error: 'Failed to uncancel subscription at provider' }) + return + } + + // Upsert the updated subscription into our database + await accountClient.upsertSubscription(uncanceledSubscription) + + res.status(200).json(uncanceledSubscription) + }, + req, + res, + () => {} + ) + } + ) + + /** + * POST /api/v1/subscriptions/:subscriptionId/updatePlan + * Update a subscription to a different plan + * Body: { plan: string } - The new plan name (e.g., 'common', 'rare', 'epic', 'legendary') + * Authorization: Only workspace owner/admin can update + * Note: subscriptionId is the internal subscription ID, not the provider's ID + */ + app.post( + '/api/v1/subscriptions/:subscriptionId/updatePlan', + subscriptionRateLimiter, + withToken, + withLoginInfo, + withOwner, + (req: RequestWithAuth, res: Response) => { + if (provider === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + void handleRequest( + ctx, + 'update-plan', + async (ctx) => { + const subscriptionId = req.params.subscriptionId + const { plan } = req.body + const loginInfo = req.loginInfo as WorkspaceLoginInfo + + if (plan === undefined || typeof plan !== 'string') { + res.status(400).json({ error: 'Missing or invalid field: plan' }) + return + } + + if (loginInfo?.workspaceUrl === undefined) { + res.status(401).json({ error: 'Missing workspace url in login info' }) + return + } + + // Get subscription from our database using internal ID + const subscription = await accountClient.getSubscriptionById(subscriptionId) + + if (subscription === undefined || subscription === null) { + res.status(404).json({ error: 'Subscription not found' }) + return + } + + let updateResult: SubscriptionData | CheckoutResponse | null + + try { + // Update via provider using the provider's subscription ID + updateResult = await provider.updateSubscriptionPlan( + ctx, + subscription.providerSubscriptionId, + plan, + loginInfo.workspaceUrl + ) + } catch (err) { + ctx.error('Failed to update subscription at provider', { err }) + res.status(500).json({ error: 'Failed to update subscription at provider' }) + return + } + + if (updateResult === null) { + res.status(404).json({ error: 'Failed to update subscription at provider' }) + return + } + + // Check if it's a CheckoutResponse (free-to-paid upgrade) + if ('checkoutUrl' in updateResult) { + // Return checkout response for free-to-paid upgrades + res.status(200).json(updateResult) + return + } + + // It's a SubscriptionData - update was direct + // Upsert the updated subscription into our database + await accountClient.upsertSubscription(updateResult) + + res.status(200).json(updateResult) + }, + req, + res, + () => {} + ) + } + ) + + /** + * GET /api/v1/subscriptions/:subscriptionId + * Get subscription details + * Authorization: Only admin can view billing details directly from provider + * (Subscription contains sensitive pricing and payment information) + */ + app.get('/api/v1/subscriptions/:subscriptionId', withToken, withAdmin, (req: RequestWithAuth, res: Response) => { + if (provider === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + void handleRequest( + ctx, + 'get-subscription', + async (ctx) => { + const subscriptionId = req.params.subscriptionId + const subscription = await provider.getSubscription(ctx, subscriptionId) + res.status(200).json(subscription) + }, + req, + res, + () => {} + ) + }) + + /** + * GET /api/v1/checkouts/:checkoutId/status + * Get subscription status by checkout ID + * Used to poll for subscription creation after successful checkout + * If subscription is found in Polar but not in our DB, it will be upserted + * If it exists in DB but has changed (newer modifiedAt), it will be updated + * Authorization: Only authenticated workspace owners can check checkout status + */ + app.get('/api/v1/checkouts/:checkoutId/status', withToken, withOwner, (req: RequestWithAuth, res: Response) => { + if (provider === undefined) { + res.status(503).json({ error: 'Payment provider is not configured' }) + return + } + + // Disable caching for this endpoint - we want fresh data on every poll + res.set('Cache-Control', 'no-cache, no-store, must-revalidate') + res.set('Pragma', 'no-cache') + res.set('Expires', '0') + + void handleRequest( + ctx, + 'checkout-subscription-status', + async (ctx) => { + const checkoutId = req.params.checkoutId + const accountClient = getAccountClient(config.AccountsUrl, serviceToken) + + // Try to get subscription from Polar by checkout ID + const subscriptionData = await provider.getSubscriptionByCheckout(ctx, checkoutId) + + if (subscriptionData !== null) { + // Subscription exists in Polar - check if we need to update our DB + try { + // Get existing subscription from our DB if it exists + const existingSubscription = await accountClient.getSubscriptionByProviderId( + subscriptionData.provider, + subscriptionData.providerSubscriptionId + ) + + // Check if we should upsert (doesn't exist or has changed) + const shouldUpsert = + existingSubscription === null || + (subscriptionData.providerData?.modifiedAt !== undefined && + (existingSubscription?.providerData?.modifiedAt ?? 0) < subscriptionData.providerData.modifiedAt) + + if (shouldUpsert) { + await accountClient.upsertSubscription(subscriptionData) + ctx.info('Subscription upserted from checkout poll', { + checkoutId, + subscriptionId: subscriptionData.id, + isNew: existingSubscription === null + }) + } + } catch (err) { + ctx.error('Failed to sync subscription to DB', { checkoutId, err }) + // Still return the subscription data even if DB update failed + } + + res.status(200).json({ + checkoutId, + subscriptionId: subscriptionData.id, + status: 'completed', + subscription: subscriptionData + }) + return + } + + // Subscription not yet completed in Polar + res.status(200).json({ + checkoutId, + subscriptionId: null, + status: 'pending', + subscription: null + }) + }, + req, + res, + () => {} + ) + }) + + app.use((_req, res) => { + res.status(404).json({ message: 'Not Found' }) + }) + + return { + app, + close: () => { + stopReconciliation() + } + } +} + +export function listen (e: Express, port: number, host?: string): Server { + const cb = (): void => { + console.log(`Service started at ${host ?? '*'}:${port}`) + } + + const server = host !== undefined ? e.listen(port, host, cb) : e.listen(port, cb) + server.keepAliveTimeout = KEEP_ALIVE_TIMEOUT * 1000 + 1000 + server.headersTimeout = KEEP_ALIVE_TIMEOUT * 1000 + 2000 + + return server +} diff --git a/services/payment/pod-payment/src/utils.ts b/services/payment/pod-payment/src/utils.ts new file mode 100644 index 0000000000..fc8060df57 --- /dev/null +++ b/services/payment/pod-payment/src/utils.ts @@ -0,0 +1,27 @@ +// +// Copyright © 2025 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import { getClient, type SubscriptionType, type AccountClient } from '@hcengineering/account-client' + +/** + * Get account client for service operations + */ +export function getAccountClient (accountsUrl: string, serviceToken: string): AccountClient { + return getClient(accountsUrl, serviceToken) +} + +export function getPlanKey (type: SubscriptionType, plan: string): string { + return `${plan}@${type}` +} diff --git a/services/payment/pod-payment/tsconfig.json b/services/payment/pod-payment/tsconfig.json new file mode 100644 index 0000000000..bde4944748 --- /dev/null +++ b/services/payment/pod-payment/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./node_modules/@hcengineering/platform-rig/profiles/default/tsconfig.json", + + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib", + "declarationDir": "./types", + "tsBuildInfoFile": ".build/build.tsbuildinfo", + "module": "node16", + "moduleResolution": "node16" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "lib", "dist", "types", "bundle"] +} \ No newline at end of file diff --git a/services/print/pod-print/package.json b/services/print/pod-print/package.json index 1e3110cca9..7549f09d52 100644 --- a/services/print/pod-print/package.json +++ b/services/print/pod-print/package.json @@ -59,7 +59,7 @@ "@hcengineering/server-storage": "^0.7.15", "@hcengineering/core": "^0.7.18", "@hcengineering/platform": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "cors": "^2.8.5", "dotenv": "~16.0.0", "express": "^4.21.2", diff --git a/services/process/package.json b/services/process/package.json index 88f4d9c9ae..d230447c16 100644 --- a/services/process/package.json +++ b/services/process/package.json @@ -65,7 +65,7 @@ "@hcengineering/server-process-resources": "^0.7.0", "@hcengineering/server-core": "^0.7.16", "@hcengineering/server-token": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/communication-types": "^0.7.12", "@hcengineering/communication-sdk-types": "^0.7.12", "@temporalio/client": "1.12.3", diff --git a/services/rating/package.json b/services/rating/package.json index cd6a34b8e3..6dd9aa98d5 100644 --- a/services/rating/package.json +++ b/services/rating/package.json @@ -67,7 +67,7 @@ "@koa/cors": "^5.0.0", "@hcengineering/middleware": "^0.7.15", "@hcengineering/server-client": "^0.7.15", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/server-storage": "^0.7.15", "@hcengineering/postgres": "^0.7.15", "@hcengineering/mongo": "^0.7.15", diff --git a/services/sign/pod-sign/package.json b/services/sign/pod-sign/package.json index 07d5482d56..09735b59a3 100644 --- a/services/sign/pod-sign/package.json +++ b/services/sign/pod-sign/package.json @@ -62,7 +62,7 @@ "@hcengineering/client": "^0.7.17", "@hcengineering/client-resources": "^0.7.17", "@hcengineering/server-client": "^0.7.15", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "cors": "^2.8.5", "dotenv": "~16.0.0", "express": "^4.21.2", diff --git a/services/telegram-bot/pod-telegram-bot/package.json b/services/telegram-bot/pod-telegram-bot/package.json index 062b14d758..021e74095c 100644 --- a/services/telegram-bot/pod-telegram-bot/package.json +++ b/services/telegram-bot/pod-telegram-bot/package.json @@ -55,7 +55,7 @@ "dependencies": { "@hcengineering/activity": "^0.7.0", "@hcengineering/analytics": "^0.7.17", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/analytics-service": "^0.7.17", "@hcengineering/attachment": "^0.7.0", "@hcengineering/chunter": "^0.7.0", diff --git a/services/translate/package.json b/services/translate/package.json index 5b3285f872..afc55e7c3c 100644 --- a/services/translate/package.json +++ b/services/translate/package.json @@ -52,7 +52,7 @@ "typescript": "^5.9.3" }, "dependencies": { - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/analytics": "^0.7.17", "@hcengineering/analytics-service": "^0.7.17", "@hcengineering/api-client": "^0.7.18", diff --git a/tests/sanity/package.json b/tests/sanity/package.json index 6078860033..ce779f62a2 100644 --- a/tests/sanity/package.json +++ b/tests/sanity/package.json @@ -52,7 +52,7 @@ "@hcengineering/core": "^0.7.18", "@hcengineering/client-resources": "^0.7.17", "@hcengineering/account": "^0.7.0", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/server-token": "^0.7.17" } } diff --git a/ws-tests/api-tests/package.json b/ws-tests/api-tests/package.json index 00c041920c..29fa171bc8 100644 --- a/ws-tests/api-tests/package.json +++ b/ws-tests/api-tests/package.json @@ -51,7 +51,7 @@ "ws": "^8.18.2", "snappyjs": "^0.7.0", "@hcengineering/api-client": "^0.7.18", - "@hcengineering/account-client": "^0.7.17", + "@hcengineering/account-client": "^0.7.18", "@hcengineering/tracker": "^0.7.0", "@hcengineering/task": "^0.7.0", "@hcengineering/contact": "^0.7.0",