Просмотр исходного кода

Merge pull request #15172 from overleaf/em-promise-utils

Move util/promises from web into a shared library

GitOrigin-RevId: fe1980dc57b9dc8ce86fa1fad6a8a817e9505b3d
Eric Mc Sween 2 лет назад
Родитель
Сommit
680ebae30b
95 измененных файлов с 229 добавлено и 487 удалено
  1. 129 19
      package-lock.json
  2. 1 1
      services/web/app/src/Features/Analytics/AnalyticsManager.js
  3. 1 1
      services/web/app/src/Features/Analytics/AnalyticsRouter.js
  4. 1 1
      services/web/app/src/Features/Authorization/AuthorizationMiddleware.js
  5. 1 1
      services/web/app/src/Features/Captcha/CaptchaMiddleware.js
  6. 1 1
      services/web/app/src/Features/Collaborators/CollaboratorsController.js
  7. 1 1
      services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js
  8. 1 1
      services/web/app/src/Features/Compile/ClsiCookieManager.js
  9. 1 1
      services/web/app/src/Features/Compile/ClsiFormatChecker.js
  10. 1 1
      services/web/app/src/Features/Compile/ClsiManager.js
  11. 1 1
      services/web/app/src/Features/Compile/CompileController.js
  12. 1 1
      services/web/app/src/Features/Contacts/ContactController.js
  13. 1 1
      services/web/app/src/Features/Cooldown/CooldownManager.js
  14. 1 1
      services/web/app/src/Features/Docstore/DocstoreManager.js
  15. 1 1
      services/web/app/src/Features/Documents/DocumentController.js
  16. 1 1
      services/web/app/src/Features/Editor/EditorController.js
  17. 1 1
      services/web/app/src/Features/Editor/EditorHttpController.js
  18. 1 1
      services/web/app/src/Features/FileStore/FileStoreHandler.js
  19. 1 1
      services/web/app/src/Features/Institutions/InstitutionsAPI.js
  20. 1 1
      services/web/app/src/Features/Institutions/InstitutionsFeatures.js
  21. 1 1
      services/web/app/src/Features/LinkedFiles/LinkedFilesHandler.js
  22. 1 1
      services/web/app/src/Features/LinkedFiles/UrlAgent.js
  23. 1 1
      services/web/app/src/Features/Notifications/NotificationsBuilder.js
  24. 1 1
      services/web/app/src/Features/Notifications/NotificationsHandler.js
  25. 1 1
      services/web/app/src/Features/PasswordReset/PasswordResetController.js
  26. 1 1
      services/web/app/src/Features/Project/ProjectAuditLogHandler.js
  27. 1 1
      services/web/app/src/Features/Project/ProjectDeleter.js
  28. 1 1
      services/web/app/src/Features/Project/ProjectDuplicator.js
  29. 1 1
      services/web/app/src/Features/Project/ProjectEntityHandler.js
  30. 1 1
      services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js
  31. 1 1
      services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js
  32. 1 1
      services/web/app/src/Features/Project/ProjectGetter.js
  33. 1 1
      services/web/app/src/Features/Project/ProjectHistoryHandler.js
  34. 1 1
      services/web/app/src/Features/Project/ProjectListController.js
  35. 1 1
      services/web/app/src/Features/Project/ProjectLocator.js
  36. 1 1
      services/web/app/src/Features/Project/ProjectOptionsHandler.js
  37. 1 1
      services/web/app/src/Features/Project/ProjectRootDocManager.js
  38. 1 1
      services/web/app/src/Features/Project/ProjectUpdateHandler.js
  39. 1 1
      services/web/app/src/Features/Publishers/PublishersGetter.js
  40. 1 1
      services/web/app/src/Features/Referal/ReferalAllocator.js
  41. 1 1
      services/web/app/src/Features/Security/LoginRateLimiter.js
  42. 1 1
      services/web/app/src/Features/Security/OneTimeTokenHandler.js
  43. 1 1
      services/web/app/src/Features/Spelling/SpellingHandler.js
  44. 1 1
      services/web/app/src/Features/Subscription/FeaturesUpdater.js
  45. 1 1
      services/web/app/src/Features/Subscription/LimitationsManager.js
  46. 1 1
      services/web/app/src/Features/Subscription/SubscriptionController.js
  47. 1 1
      services/web/app/src/Features/Subscription/SubscriptionHandler.js
  48. 1 1
      services/web/app/src/Features/Subscription/SubscriptionUpdater.js
  49. 1 1
      services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js
  50. 1 1
      services/web/app/src/Features/Subscription/TeamInvitesController.js
  51. 4 1
      services/web/app/src/Features/Subscription/TeamInvitesHandler.js
  52. 1 1
      services/web/app/src/Features/Subscription/UserFeaturesUpdater.js
  53. 1 1
      services/web/app/src/Features/Subscription/V1SubscriptionManager.js
  54. 1 1
      services/web/app/src/Features/Survey/SurveyHandler.js
  55. 1 1
      services/web/app/src/Features/Tags/TagsController.js
  56. 1 1
      services/web/app/src/Features/Tags/TagsHandler.js
  57. 1 1
      services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js
  58. 1 1
      services/web/app/src/Features/TokenAccess/TokenAccessController.js
  59. 1 1
      services/web/app/src/Features/TokenAccess/TokenAccessHandler.js
  60. 1 1
      services/web/app/src/Features/Tutorial/TutorialController.js
  61. 1 1
      services/web/app/src/Features/Uploads/ArchiveManager.js
  62. 1 1
      services/web/app/src/Features/Uploads/FileTypeManager.js
  63. 1 1
      services/web/app/src/Features/User/SAMLIdentityManager.js
  64. 1 1
      services/web/app/src/Features/User/ThirdPartyIdentityManager.js
  65. 1 1
      services/web/app/src/Features/User/UserController.js
  66. 1 1
      services/web/app/src/Features/User/UserEmailsController.js
  67. 1 1
      services/web/app/src/Features/User/UserGetter.js
  68. 1 1
      services/web/app/src/Features/User/UserInfoManager.js
  69. 1 1
      services/web/app/src/Features/User/UserPagesController.js
  70. 1 1
      services/web/app/src/Features/UserMembership/UserMembershipHandler.js
  71. 1 1
      services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js
  72. 1 1
      services/web/app/src/Features/UserMembership/UserMembershipsHandler.js
  73. 1 1
      services/web/app/src/Features/V1/V1Api.js
  74. 1 1
      services/web/app/src/infrastructure/ExpressLocals.js
  75. 1 1
      services/web/app/src/infrastructure/FileWriter.js
  76. 1 1
      services/web/app/src/infrastructure/GeoIpLookup.js
  77. 6 166
      services/web/app/src/util/promises.js
  78. 1 0
      services/web/package.json
  79. 1 1
      services/web/scripts/back_fill_deleted_files.js
  80. 1 1
      services/web/scripts/back_fill_doc_name_for_deleted_docs.js
  81. 1 1
      services/web/scripts/backfill_mixpanel_user_properties.js
  82. 1 1
      services/web/scripts/backfill_user_properties.js
  83. 1 1
      services/web/scripts/bench_bcrypt.js
  84. 1 1
      services/web/scripts/clear_sessions_2fa.js
  85. 1 1
      services/web/scripts/convert_archived_state.js
  86. 1 1
      services/web/scripts/delete_orphaned_chat_threads.js
  87. 1 1
      services/web/scripts/delete_orphaned_data_helper.js
  88. 1 1
      services/web/scripts/delete_orphaned_doc_comment_ranges.js
  89. 1 1
      services/web/scripts/delete_orphaned_docs_online_check.js
  90. 1 1
      services/web/scripts/delete_orphaned_project_archives.js
  91. 1 1
      services/web/scripts/migrate_audit_logs.js
  92. 1 1
      services/web/scripts/regenerate_duplicate_referral_ids.js
  93. 1 1
      services/web/test/acceptance/src/helpers/RecurlySubscription.js
  94. 0 1
      services/web/test/unit/bootstrap.js
  95. 0 211
      services/web/test/unit/src/util/promisesTests.js

+ 129 - 19
package-lock.json

@@ -502,6 +502,62 @@
         "mocha": "^5.2.0"
       }
     },
+    "libraries/promise-utils": {
+      "name": "@overleaf/promise-utils",
+      "version": "0.1.0",
+      "license": "AGPL-3.0-only",
+      "dependencies": {
+        "p-limit": "^2.3.0"
+      },
+      "devDependencies": {
+        "chai": "^4.3.10",
+        "mocha": "^10.2.0"
+      }
+    },
+    "libraries/promise-utils/node_modules/chai": {
+      "version": "4.3.10",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
+      "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==",
+      "dev": true,
+      "dependencies": {
+        "assertion-error": "^1.1.0",
+        "check-error": "^1.0.3",
+        "deep-eql": "^4.1.3",
+        "get-func-name": "^2.0.2",
+        "loupe": "^2.3.6",
+        "pathval": "^1.1.1",
+        "type-detect": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "libraries/promise-utils/node_modules/deep-eql": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+      "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+      "dev": true,
+      "dependencies": {
+        "type-detect": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "libraries/promise-utils/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "libraries/ranges-tracker": {
       "name": "@overleaf/ranges-tracker",
       "devDependencies": {
@@ -7636,6 +7692,10 @@
       "resolved": "services/project-history",
       "link": true
     },
+    "node_modules/@overleaf/promise-utils": {
+      "resolved": "libraries/promise-utils",
+      "link": true
+    },
     "node_modules/@overleaf/ranges-tracker": {
       "resolved": "libraries/ranges-tracker",
       "link": true
@@ -16134,10 +16194,13 @@
       }
     },
     "node_modules/check-error": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
-      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+      "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
       "dev": true,
+      "dependencies": {
+        "get-func-name": "^2.0.2"
+      },
       "engines": {
         "node": "*"
       }
@@ -22348,9 +22411,9 @@
       }
     },
     "node_modules/get-func-name": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+      "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
       "dev": true,
       "engines": {
         "node": "*"
@@ -27039,9 +27102,9 @@
       }
     },
     "node_modules/loupe": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.1.tgz",
-      "integrity": "sha512-EN1D3jyVmaX4tnajVlfbREU4axL647hLec1h/PXAb8CPDMJiYitcWF2UeLVNttRqaIqQs4x+mRvXf+d+TlDrCA==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
+      "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
       "dev": true,
       "dependencies": {
         "get-func-name": "^2.0.0"
@@ -39214,6 +39277,7 @@
         "@overleaf/metrics": "*",
         "@overleaf/o-error": "*",
         "@overleaf/object-persistor": "*",
+        "@overleaf/promise-utils": "*",
         "@overleaf/redis-wrapper": "*",
         "@overleaf/settings": "*",
         "@slack/webhook": "^6.1.0",
@@ -46968,6 +47032,48 @@
         }
       }
     },
+    "@overleaf/promise-utils": {
+      "version": "file:libraries/promise-utils",
+      "requires": {
+        "chai": "^4.3.10",
+        "mocha": "^10.2.0",
+        "p-limit": "^2.3.0"
+      },
+      "dependencies": {
+        "chai": {
+          "version": "4.3.10",
+          "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz",
+          "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==",
+          "dev": true,
+          "requires": {
+            "assertion-error": "^1.1.0",
+            "check-error": "^1.0.3",
+            "deep-eql": "^4.1.3",
+            "get-func-name": "^2.0.2",
+            "loupe": "^2.3.6",
+            "pathval": "^1.1.1",
+            "type-detect": "^4.0.8"
+          }
+        },
+        "deep-eql": {
+          "version": "4.1.3",
+          "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
+          "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==",
+          "dev": true,
+          "requires": {
+            "type-detect": "^4.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        }
+      }
+    },
     "@overleaf/ranges-tracker": {
       "version": "file:libraries/ranges-tracker",
       "requires": {
@@ -47452,6 +47558,7 @@
         "@overleaf/metrics": "*",
         "@overleaf/o-error": "*",
         "@overleaf/object-persistor": "*",
+        "@overleaf/promise-utils": "*",
         "@overleaf/ranges-tracker": "*",
         "@overleaf/redis-wrapper": "*",
         "@overleaf/settings": "*",
@@ -54897,10 +55004,13 @@
       "requires": {}
     },
     "check-error": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
-      "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
-      "dev": true
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
+      "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
+      "dev": true,
+      "requires": {
+        "get-func-name": "^2.0.2"
+      }
     },
     "check-more-types": {
       "version": "2.24.0",
@@ -59536,9 +59646,9 @@
       "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
     },
     "get-func-name": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
-      "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+      "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
       "dev": true
     },
     "get-intrinsic": {
@@ -63152,9 +63262,9 @@
       }
     },
     "loupe": {
-      "version": "2.3.1",
-      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.1.tgz",
-      "integrity": "sha512-EN1D3jyVmaX4tnajVlfbREU4axL647hLec1h/PXAb8CPDMJiYitcWF2UeLVNttRqaIqQs4x+mRvXf+d+TlDrCA==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
+      "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==",
       "dev": true,
       "requires": {
         "get-func-name": "^2.0.0"

+ 1 - 1
services/web/app/src/Features/Analytics/AnalyticsManager.js

@@ -5,7 +5,7 @@ const Metrics = require('../../infrastructure/Metrics')
 const Queues = require('../../infrastructure/Queues')
 const crypto = require('crypto')
 const _ = require('lodash')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const logger = require('@overleaf/logger')
 const { getAnalyticsIdFromMongoUser } = require('./AnalyticsHelper')
 

+ 1 - 1
services/web/app/src/Features/Analytics/AnalyticsRouter.js

@@ -3,7 +3,7 @@ const AnalyticsController = require('./AnalyticsController')
 const AnalyticsProxy = require('./AnalyticsProxy')
 const { RateLimiter } = require('../../infrastructure/RateLimiter')
 const RateLimiterMiddleware = require('../Security/RateLimiterMiddleware')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 const rateLimiters = {
   recordEvent: new RateLimiter('analytics-record-event', {

+ 1 - 1
services/web/app/src/Features/Authorization/AuthorizationMiddleware.js

@@ -6,7 +6,7 @@ const HttpErrorHandler = require('../Errors/HttpErrorHandler')
 const AuthenticationController = require('../Authentication/AuthenticationController')
 const SessionManager = require('../Authentication/SessionManager')
 const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const {
   canRedirectToAdminDomain,
 } = require('../Helpers/AdminAuthorizationHelper')

+ 1 - 1
services/web/app/src/Features/Captcha/CaptchaMiddleware.js

@@ -5,7 +5,7 @@ const Metrics = require('@overleaf/metrics')
 const OError = require('@overleaf/o-error')
 const DeviceHistory = require('./DeviceHistory')
 const AuthenticationController = require('../Authentication/AuthenticationController')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 function respondInvalidCaptcha(req, res) {
   res.status(400).json({

+ 1 - 1
services/web/app/src/Features/Collaborators/CollaboratorsController.js

@@ -9,7 +9,7 @@ const EditorRealTimeController = require('../Editor/EditorRealTimeController')
 const TagsHandler = require('../Tags/TagsHandler')
 const Errors = require('../Errors/Errors')
 const logger = require('@overleaf/logger')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper')
 const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
 const ProjectAuditLogHandler = require('../Project/ProjectAuditLogHandler')

+ 1 - 1
services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js

@@ -11,7 +11,7 @@ const EditorRealTimeController = require('../Editor/EditorRealTimeController')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const SessionManager = require('../Authentication/SessionManager')
 const { RateLimiter } = require('../../infrastructure/RateLimiter')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const ProjectAuditLogHandler = require('../Project/ProjectAuditLogHandler')
 const Errors = require('../Errors/Errors')
 

+ 1 - 1
services/web/app/src/Features/Compile/ClsiCookieManager.js

@@ -6,7 +6,7 @@ const RedisWrapper = require('../../infrastructure/RedisWrapper')
 const Cookie = require('cookie')
 const logger = require('@overleaf/logger')
 const Metrics = require('@overleaf/metrics')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const clsiCookiesEnabled = (Settings.clsiCookie?.key ?? '') !== ''
 

+ 1 - 1
services/web/app/src/Features/Compile/ClsiFormatChecker.js

@@ -13,7 +13,7 @@ let ClsiFormatChecker
 const _ = require('lodash')
 const async = require('async')
 const settings = require('@overleaf/settings')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 module.exports = ClsiFormatChecker = {
   checkRecoursesForProblems(resources, callback) {

+ 1 - 1
services/web/app/src/Features/Compile/ClsiManager.js

@@ -1,5 +1,5 @@
 const { callbackify } = require('util')
-const { callbackifyMultiResult } = require('../../util/promises')
+const { callbackifyMultiResult } = require('@overleaf/promise-utils')
 const {
   fetchString,
   fetchStringWithResponse,

+ 1 - 1
services/web/app/src/Features/Compile/CompileController.js

@@ -17,7 +17,7 @@ const ClsiCookieManager = require('./ClsiCookieManager')(
 const Path = require('path')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const SplitTestHandler = require('../SplitTests/SplitTestHandler')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 const {
   fetchStreamWithResponse,
   RequestFailedError,

+ 1 - 1
services/web/app/src/Features/Contacts/ContactController.js

@@ -2,7 +2,7 @@ const SessionManager = require('../Authentication/SessionManager')
 const ContactManager = require('./ContactManager')
 const UserGetter = require('../User/UserGetter')
 const Modules = require('../../infrastructure/Modules')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 function _formatContact(contact) {
   return {

+ 1 - 1
services/web/app/src/Features/Cooldown/CooldownManager.js

@@ -14,7 +14,7 @@ let CooldownManager
 const RedisWrapper = require('../../infrastructure/RedisWrapper')
 const rclient = RedisWrapper.client('cooldown')
 const logger = require('@overleaf/logger')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const COOLDOWN_IN_SECONDS = 60 * 10
 

+ 1 - 1
services/web/app/src/Features/Docstore/DocstoreManager.js

@@ -1,5 +1,5 @@
 const { promisify } = require('util')
-const { promisifyMultiResult } = require('../../util/promises')
+const { promisifyMultiResult } = require('@overleaf/promise-utils')
 const request = require('request').defaults({ jar: false })
 const OError = require('@overleaf/o-error')
 const logger = require('@overleaf/logger')

+ 1 - 1
services/web/app/src/Features/Documents/DocumentController.js

@@ -5,7 +5,7 @@ const ProjectEntityUpdateHandler = require('../Project/ProjectEntityUpdateHandle
 const logger = require('@overleaf/logger')
 const _ = require('lodash')
 const { plainTextResponse } = require('../../infrastructure/Response')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 async function getDocument(req, res) {
   const { Project_id: projectId, doc_id: docId } = req.params

+ 1 - 1
services/web/app/src/Features/Editor/EditorController.js

@@ -8,7 +8,7 @@ const ProjectDeleter = require('../Project/ProjectDeleter')
 const EditorRealTimeController = require('./EditorRealTimeController')
 const async = require('async')
 const PublicAccessLevels = require('../Authorization/PublicAccessLevels')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const EditorController = {
   addDoc(projectId, folderId, docName, docLines, source, userId, callback) {

+ 1 - 1
services/web/app/src/Features/Editor/EditorHttpController.js

@@ -13,7 +13,7 @@ const SessionManager = require('../Authentication/SessionManager')
 const Errors = require('../Errors/Errors')
 const DocstoreManager = require('../Docstore/DocstoreManager')
 const logger = require('@overleaf/logger')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 const {
   NEW_COMPILE_TIMEOUT_ENFORCED_CUTOFF,

+ 1 - 1
services/web/app/src/Features/FileStore/FileStoreHandler.js

@@ -8,7 +8,7 @@ const FileHashManager = require('./FileHashManager')
 const { File } = require('../../models/File')
 const Errors = require('../Errors/Errors')
 const OError = require('@overleaf/o-error')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ONE_MIN_IN_MS = 60 * 1000
 const FIVE_MINS_IN_MS = ONE_MIN_IN_MS * 5

+ 1 - 1
services/web/app/src/Features/Institutions/InstitutionsAPI.js

@@ -3,7 +3,7 @@ const logger = require('@overleaf/logger')
 const metrics = require('@overleaf/metrics')
 const settings = require('@overleaf/settings')
 const request = require('requestretry')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
 const {
   V1ConnectionError,

+ 1 - 1
services/web/app/src/Features/Institutions/InstitutionsFeatures.js

@@ -2,7 +2,7 @@ let InstitutionsFeatures
 const UserGetter = require('../User/UserGetter')
 const PlansLocator = require('../Subscription/PlansLocator')
 const Settings = require('@overleaf/settings')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 module.exports = InstitutionsFeatures = {
   getInstitutionsFeatures(userId, callback) {

+ 1 - 1
services/web/app/src/Features/LinkedFiles/LinkedFilesHandler.js

@@ -21,7 +21,7 @@ const {
   V1ProjectNotFoundError,
   BadDataError,
 } = require('./LinkedFilesErrors')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const LinkedFilesHandler = {
   getFileById(projectId, fileId, callback) {

+ 1 - 1
services/web/app/src/Features/LinkedFiles/UrlAgent.js

@@ -4,7 +4,7 @@ const { InvalidUrlError, UrlFetchFailedError } = require('./LinkedFilesErrors')
 const LinkedFilesHandler = require('./LinkedFilesHandler')
 const UrlHelper = require('../Helpers/UrlHelper')
 const { fetchStream, RequestFailedError } = require('@overleaf/fetch-utils')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 const { FileTooLargeError } = require('../Errors/Errors')
 
 async function createLinkedFile(

+ 1 - 1
services/web/app/src/Features/Notifications/NotificationsBuilder.js

@@ -1,5 +1,5 @@
 const NotificationsHandler = require('./NotificationsHandler')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const request = require('request')
 const settings = require('@overleaf/settings')
 

+ 1 - 1
services/web/app/src/Features/Notifications/NotificationsHandler.js

@@ -2,7 +2,7 @@ const settings = require('@overleaf/settings')
 const request = require('request')
 const logger = require('@overleaf/logger')
 const _ = require('lodash')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const notificationsApi = _.get(settings, ['apis', 'notifications', 'url'])
 const oneSecond = 1000

+ 1 - 1
services/web/app/src/Features/PasswordReset/PasswordResetController.js

@@ -7,7 +7,7 @@ const UserUpdater = require('../User/UserUpdater')
 const UserSessionsManager = require('../User/UserSessionsManager')
 const OError = require('@overleaf/o-error')
 const EmailsHelper = require('../Helpers/EmailHelper')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 async function setNewUserPassword(req, res, next) {
   let user

+ 1 - 1
services/web/app/src/Features/Project/ProjectAuditLogHandler.js

@@ -1,6 +1,6 @@
 const logger = require('@overleaf/logger')
 const { ProjectAuditLogEntry } = require('../../models/ProjectAuditLogEntry')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 
 module.exports = {
   promises: {

+ 1 - 1
services/web/app/src/Features/Project/ProjectDeleter.js

@@ -20,7 +20,7 @@ const FilestoreHandler = require('../FileStore/FileStoreHandler')
 const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
 const ChatApiHandler = require('../Chat/ChatApiHandler')
 const moment = require('moment')
-const { promiseMapWithLimit } = require('../../util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { READ_PREFERENCE_SECONDARY } = require('../../infrastructure/mongodb')
 
 const EXPIRE_PROJECTS_AFTER_DAYS = 90

+ 1 - 1
services/web/app/src/Features/Project/ProjectDuplicator.js

@@ -1,7 +1,7 @@
 const { callbackify } = require('util')
 const Path = require('path')
 const OError = require('@overleaf/o-error')
-const { promiseMapWithLimit } = require('../../util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { Doc } = require('../../models/Doc')
 const { File } = require('../../models/File')
 const DocstoreManager = require('../Docstore/DocstoreManager')

+ 1 - 1
services/web/app/src/Features/Project/ProjectEntityHandler.js

@@ -2,7 +2,7 @@ const path = require('path')
 const DocstoreManager = require('../Docstore/DocstoreManager')
 const Errors = require('../Errors/Errors')
 const ProjectGetter = require('./ProjectGetter')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const OError = require('@overleaf/o-error')
 const { iterablePaths } = require('./IterablePath')
 

+ 1 - 1
services/web/app/src/Features/Project/ProjectEntityMongoUpdateHandler.js

@@ -1,5 +1,5 @@
 const { callbackify } = require('util')
-const { callbackifyMultiResult } = require('../../util/promises')
+const { callbackifyMultiResult } = require('@overleaf/promise-utils')
 const _ = require('underscore')
 const logger = require('@overleaf/logger')
 const path = require('path')

+ 1 - 1
services/web/app/src/Features/Project/ProjectEntityUpdateHandler.js

@@ -21,7 +21,7 @@ const SafePath = require('./SafePath')
 const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
 const FileWriter = require('../../infrastructure/FileWriter')
 const EditorRealTimeController = require('../Editor/EditorRealTimeController')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const { iterablePaths } = require('./IterablePath')
 
 const LOCK_NAMESPACE = 'sequentialProjectStructureUpdateLock'

+ 1 - 1
services/web/app/src/Features/Project/ProjectGetter.js

@@ -2,7 +2,7 @@ const { db } = require('../../infrastructure/mongodb')
 const { normalizeQuery } = require('../Helpers/Mongo')
 const OError = require('@overleaf/o-error')
 const metrics = require('@overleaf/metrics')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const { Project } = require('../../models/Project')
 const logger = require('@overleaf/logger')
 const LockManager = require('../../infrastructure/LockManager')

+ 1 - 1
services/web/app/src/Features/Project/ProjectHistoryHandler.js

@@ -2,7 +2,7 @@ const { Project } = require('../../models/Project')
 const ProjectDetailsHandler = require('./ProjectDetailsHandler')
 const HistoryManager = require('../History/HistoryManager')
 const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ProjectHistoryHandler = {
   setHistoryId(projectId, historyId, callback) {

+ 1 - 1
services/web/app/src/Features/Project/ProjectListController.js

@@ -9,7 +9,7 @@ const Sources = require('../Authorization/Sources')
 const UserGetter = require('../User/UserGetter')
 const SurveyHandler = require('../Survey/SurveyHandler')
 const TagsHandler = require('../Tags/TagsHandler')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const logger = require('@overleaf/logger')
 const Features = require('../../infrastructure/Features')
 const SubscriptionViewModelBuilder = require('../Subscription/SubscriptionViewModelBuilder')

+ 1 - 1
services/web/app/src/Features/Project/ProjectLocator.js

@@ -4,7 +4,7 @@ const OError = require('@overleaf/o-error')
 const async = require('async')
 const ProjectGetter = require('./ProjectGetter')
 const Errors = require('../Errors/Errors')
-const { promisifyMultiResult } = require('../../util/promises')
+const { promisifyMultiResult } = require('@overleaf/promise-utils')
 const { iterablePaths } = require('./IterablePath')
 
 function findElement(options, _callback) {

+ 1 - 1
services/web/app/src/Features/Project/ProjectOptionsHandler.js

@@ -1,6 +1,6 @@
 const { Project } = require('../../models/Project')
 const settings = require('@overleaf/settings')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const safeCompilers = ['xelatex', 'pdflatex', 'latex', 'lualatex']
 

+ 1 - 1
services/web/app/src/Features/Project/ProjectRootDocManager.js

@@ -23,7 +23,7 @@ const { promisify } = require('util')
 const async = require('async')
 const globby = require('globby')
 const _ = require('underscore')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 module.exports = ProjectRootDocManager = {
   setRootDocAutomatically(projectId, callback) {

+ 1 - 1
services/web/app/src/Features/Project/ProjectUpdateHandler.js

@@ -11,7 +11,7 @@
  */
 const { Project } = require('../../models/Project')
 const logger = require('@overleaf/logger')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ProjectUpdateHandler = {
   markAsUpdated(projectId, lastUpdatedAt, lastUpdatedBy, callback) {

+ 1 - 1
services/web/app/src/Features/Publishers/PublishersGetter.js

@@ -1,7 +1,7 @@
 const Settings = require('@overleaf/settings')
 const logger = require('@overleaf/logger')
 const { fetchJson } = require('@overleaf/fetch-utils')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 const UserMembershipsHandler = require('../UserMembership/UserMembershipsHandler')
 const UserMembershipEntityConfigs = require('../UserMembership/UserMembershipEntityConfigs')
 

+ 1 - 1
services/web/app/src/Features/Referal/ReferalAllocator.js

@@ -1,7 +1,7 @@
 const OError = require('@overleaf/o-error')
 const { User } = require('../../models/User')
 const FeaturesUpdater = require('../Subscription/FeaturesUpdater')
-const { promisify } = require('../../util/promises')
+const { promisify } = require('@overleaf/promise-utils')
 
 function allocate(
   referalId,

+ 1 - 1
services/web/app/src/Features/Security/LoginRateLimiter.js

@@ -1,5 +1,5 @@
 const { RateLimiter } = require('../../infrastructure/RateLimiter')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const rateLimiter = new RateLimiter('login', {
   points: 10,

+ 1 - 1
services/web/app/src/Features/Security/OneTimeTokenHandler.js

@@ -1,7 +1,7 @@
 const crypto = require('crypto')
 const { db } = require('../../infrastructure/mongodb')
 const Errors = require('../Errors/Errors')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ONE_HOUR_IN_S = 60 * 60
 

+ 1 - 1
services/web/app/src/Features/Spelling/SpellingHandler.js

@@ -1,6 +1,6 @@
 const OError = require('@overleaf/o-error')
 const Metrics = require('@overleaf/metrics')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const LearnedWordsManager = require('./LearnedWordsManager')
 
 module.exports = {

+ 1 - 1
services/web/app/src/Features/Subscription/FeaturesUpdater.js

@@ -1,6 +1,6 @@
 const _ = require('lodash')
 const { callbackify } = require('util')
-const { callbackifyMultiResult } = require('../../util/promises')
+const { callbackifyMultiResult } = require('@overleaf/promise-utils')
 const PlansLocator = require('./PlansLocator')
 const SubscriptionLocator = require('./SubscriptionLocator')
 const UserFeaturesUpdater = require('./UserFeaturesUpdater')

+ 1 - 1
services/web/app/src/Features/Subscription/LimitationsManager.js

@@ -8,7 +8,7 @@ const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
 const CollaboratorsInvitesHandler = require('../Collaborators/CollaboratorsInviteHandler')
 const V1SubscriptionManager = require('./V1SubscriptionManager')
 const { V1ConnectionError } = require('../Errors/Errors')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const LimitationsManager = {
   allowedNumberOfCollaboratorsInProject(projectId, callback) {

+ 1 - 1
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -14,7 +14,7 @@ const GroupPlansData = require('./GroupPlansData')
 const V1SubscriptionManager = require('./V1SubscriptionManager')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const RecurlyEventHandler = require('./RecurlyEventHandler')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const OError = require('@overleaf/o-error')
 const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 const SubscriptionHelper = require('./SubscriptionHelper')

+ 1 - 1
services/web/app/src/Features/Subscription/SubscriptionHandler.js

@@ -9,7 +9,7 @@ const LimitationsManager = require('./LimitationsManager')
 const EmailHandler = require('../Email/EmailHandler')
 const PlansLocator = require('./PlansLocator')
 const SubscriptionHelper = require('./SubscriptionHelper')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 
 function validateNoSubscriptionInRecurly(userId, callback) {
   RecurlyWrapper.listAccountActiveSubscriptions(

+ 1 - 1
services/web/app/src/Features/Subscription/SubscriptionUpdater.js

@@ -1,5 +1,5 @@
 const { db, ObjectId } = require('../../infrastructure/mongodb')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 const { Subscription } = require('../../models/Subscription')
 const SubscriptionLocator = require('./SubscriptionLocator')
 const PlansLocator = require('./PlansLocator')

+ 1 - 1
services/web/app/src/Features/Subscription/SubscriptionViewModelBuilder.js

@@ -11,7 +11,7 @@ const sanitizeHtml = require('sanitize-html')
 const _ = require('underscore')
 const async = require('async')
 const SubscriptionHelper = require('./SubscriptionHelper')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 const {
   InvalidError,
   NotFoundError,

+ 1 - 1
services/web/app/src/Features/Subscription/TeamInvitesController.js

@@ -7,7 +7,7 @@ const SubscriptionLocator = require('./SubscriptionLocator')
 const ErrorController = require('../Errors/ErrorController')
 const EmailHelper = require('../Helpers/EmailHelper')
 const UserGetter = require('../User/UserGetter')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const HttpErrorHandler = require('../Errors/HttpErrorHandler')
 const PermissionsManager = require('../Authorization/PermissionsManager')
 const EmailHandler = require('../Email/EmailHandler')

+ 4 - 1
services/web/app/src/Features/Subscription/TeamInvitesHandler.js

@@ -16,7 +16,10 @@ const EmailHandler = require('../Email/EmailHandler')
 const EmailHelper = require('../Helpers/EmailHelper')
 
 const Errors = require('../Errors/Errors')
-const { callbackify, callbackifyMultiResult } = require('../../util/promises')
+const {
+  callbackify,
+  callbackifyMultiResult,
+} = require('@overleaf/promise-utils')
 const NotificationsBuilder = require('../Notifications/NotificationsBuilder')
 
 async function getInvite(token) {

+ 1 - 1
services/web/app/src/Features/Subscription/UserFeaturesUpdater.js

@@ -1,5 +1,5 @@
 const { User } = require('../../models/User')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const Settings = require('@overleaf/settings')
 
 function _featuresChanged(newFeatures, featuresBefore) {

+ 1 - 1
services/web/app/src/Features/Subscription/V1SubscriptionManager.js

@@ -3,7 +3,7 @@ const UserGetter = require('../User/UserGetter')
 const request = require('requestretry')
 const settings = require('@overleaf/settings')
 const { V1ConnectionError, NotFoundError } = require('../Errors/Errors')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 module.exports = V1SubscriptionManager = {
   getSubscriptionsFromV1(userId, callback) {

+ 1 - 1
services/web/app/src/Features/Survey/SurveyHandler.js

@@ -1,7 +1,7 @@
 const crypto = require('crypto')
 const SurveyCache = require('./SurveyCache')
 const SubscriptionLocator = require('../Subscription/SubscriptionLocator')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 
 /**
  * @typedef {import('../../../../types/project/dashboard/survey').Survey} Survey

+ 1 - 1
services/web/app/src/Features/Tags/TagsController.js

@@ -1,7 +1,7 @@
 const TagsHandler = require('./TagsHandler')
 const SessionManager = require('../Authentication/SessionManager')
 const Errors = require('../Errors/Errors')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 async function _getTags(userId, _req, res) {
   if (!userId) {

+ 1 - 1
services/web/app/src/Features/Tags/TagsHandler.js

@@ -1,5 +1,5 @@
 const { Tag } = require('../../models/Tag')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 
 const MAX_TAG_LENGTH = 50
 

+ 1 - 1
services/web/app/src/Features/ThirdPartyDataStore/TpdsController.js

@@ -1,4 +1,4 @@
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const TpdsUpdateHandler = require('./TpdsUpdateHandler')
 const UpdateMerger = require('./UpdateMerger')
 const Errors = require('../Errors/Errors')

+ 1 - 1
services/web/app/src/Features/TokenAccess/TokenAccessController.js

@@ -5,7 +5,7 @@ const Errors = require('../Errors/Errors')
 const logger = require('@overleaf/logger')
 const settings = require('@overleaf/settings')
 const OError = require('@overleaf/o-error')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const AuthorizationManager = require('../Authorization/AuthorizationManager')
 const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
 const {

+ 1 - 1
services/web/app/src/Features/TokenAccess/TokenAccessHandler.js

@@ -6,7 +6,7 @@ const Settings = require('@overleaf/settings')
 const logger = require('@overleaf/logger')
 const V1Api = require('../V1/V1Api')
 const crypto = require('crypto')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const Analytics = require('../Analytics/AnalyticsManager')
 
 const READ_AND_WRITE_TOKEN_PATTERN = '([0-9]+[a-z]{6,12})'

+ 1 - 1
services/web/app/src/Features/Tutorial/TutorialController.js

@@ -1,6 +1,6 @@
 const SessionManager = require('../Authentication/SessionManager')
 const TutorialHandler = require('./TutorialHandler')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 const VALID_KEYS = [
   'react-history-buttons-tutorial',

+ 1 - 1
services/web/app/src/Features/Uploads/ArchiveManager.js

@@ -26,7 +26,7 @@ const {
   ZipContentsTooLargeError,
 } = require('./ArchiveErrors')
 const _ = require('underscore')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const ONE_MEG = 1024 * 1024
 

+ 1 - 1
services/web/app/src/Features/Uploads/FileTypeManager.js

@@ -1,7 +1,7 @@
 const fs = require('fs')
 const Path = require('path')
 const isUtf8 = require('utf-8-validate')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const Settings = require('@overleaf/settings')
 const Minimatch = require('minimatch').Minimatch
 

+ 1 - 1
services/web/app/src/Features/User/SAMLIdentityManager.js

@@ -10,7 +10,7 @@ const UserGetter = require('../User/UserGetter')
 const UserUpdater = require('../User/UserUpdater')
 const logger = require('@overleaf/logger')
 const { User } = require('../../models/User')
-const { promiseMapWithLimit } = require('../../util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 
 async function _addAuditLogEntry(operation, userId, auditLog, extraInfo) {
   await UserAuditLogHandler.promises.addEntry(

+ 1 - 1
services/web/app/src/Features/User/ThirdPartyIdentityManager.js

@@ -8,7 +8,7 @@ const logger = require('@overleaf/logger')
 const OError = require('@overleaf/o-error')
 const settings = require('@overleaf/settings')
 const { User } = require(`${APP_ROOT}/models/User`)
-const { promisifyAll } = require(`${APP_ROOT}/util/promises`)
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 const oauthProviders = settings.oauthProviders || {}
 

+ 1 - 1
services/web/app/src/Features/User/UserController.js

@@ -17,7 +17,7 @@ const OError = require('@overleaf/o-error')
 const EmailHandler = require('../Email/EmailHandler')
 const UrlHelper = require('../Helpers/UrlHelper')
 const { promisify } = require('util')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const {
   acceptsJson,
 } = require('../../infrastructure/RequestContentTypeDetection')

+ 1 - 1
services/web/app/src/Features/User/UserEmailsController.js

@@ -10,7 +10,7 @@ const UserEmailsConfirmationHandler = require('./UserEmailsConfirmationHandler')
 const { endorseAffiliation } = require('../Institutions/InstitutionsAPI')
 const Errors = require('../Errors/Errors')
 const HttpErrorHandler = require('../Errors/HttpErrorHandler')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const AsyncFormHelper = require('../Helpers/AsyncFormHelper')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')

+ 1 - 1
services/web/app/src/Features/User/UserGetter.js

@@ -4,7 +4,7 @@ const metrics = require('@overleaf/metrics')
 const logger = require('@overleaf/logger')
 const moment = require('moment')
 const settings = require('@overleaf/settings')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const {
   promises: InstitutionsAPIPromises,
 } = require('../Institutions/InstitutionsAPI')

+ 1 - 1
services/web/app/src/Features/User/UserInfoManager.js

@@ -1,5 +1,5 @@
 const UserGetter = require('./UserGetter')
-const { callbackify } = require('../../util/promises')
+const { callbackify } = require('@overleaf/promise-utils')
 
 async function getPersonalInfo(userId) {
   return UserGetter.promises.getUser(userId, {

+ 1 - 1
services/web/app/src/Features/User/UserPagesController.js

@@ -8,7 +8,7 @@ const SessionManager = require('../Authentication/SessionManager')
 const NewsletterManager = require('../Newsletter/NewsletterManager')
 const SubscriptionLocator = require('../Subscription/SubscriptionLocator')
 const _ = require('lodash')
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const Features = require('../../infrastructure/Features')
 const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 

+ 1 - 1
services/web/app/src/Features/UserMembership/UserMembershipHandler.js

@@ -13,7 +13,7 @@
  */
 const { ObjectId } = require('mongodb')
 const async = require('async')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const Errors = require('../Errors/Errors')
 const EntityModels = {
   Institution: require('../../models/Institution').Institution,

+ 1 - 1
services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js

@@ -1,4 +1,4 @@
-const { expressify } = require('../../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 const async = require('async')
 const UserMembershipAuthorization = require('./UserMembershipAuthorization')
 const AuthenticationController = require('../Authentication/AuthenticationController')

+ 1 - 1
services/web/app/src/Features/UserMembership/UserMembershipsHandler.js

@@ -10,7 +10,7 @@
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
 const async = require('async')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 const EntityModels = {
   Institution: require('../../models/Institution').Institution,
   Subscription: require('../../models/Subscription').Subscription,

+ 1 - 1
services/web/app/src/Features/V1/V1Api.js

@@ -10,7 +10,7 @@
 const request = require('request')
 const settings = require('@overleaf/settings')
 const Errors = require('../Errors/Errors')
-const { promisifyAll } = require('../../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 // TODO: check what happens when these settings aren't defined
 const DEFAULT_V1_PARAMS = {

+ 1 - 1
services/web/app/src/infrastructure/ExpressLocals.js

@@ -20,7 +20,7 @@ const {
 const {
   addOptionalCleanupHandlerAfterDrainingConnections,
 } = require('./GracefulShutdown')
-const { expressify } = require('../util/promises')
+const { expressify } = require('@overleaf/promise-utils')
 
 const IEEE_BRAND_ID = Settings.ieeeBrandId
 

+ 1 - 1
services/web/app/src/infrastructure/FileWriter.js

@@ -19,7 +19,7 @@ const Settings = require('@overleaf/settings')
 const request = require('request')
 const { Transform, pipeline } = require('stream')
 const { FileTooLargeError } = require('../Features/Errors/Errors')
-const { promisifyAll } = require('../util/promises')
+const { promisifyAll } = require('@overleaf/promise-utils')
 
 class SizeLimitedStream extends Transform {
   constructor(options) {

+ 1 - 1
services/web/app/src/infrastructure/GeoIpLookup.js

@@ -3,7 +3,7 @@ const settings = require('@overleaf/settings')
 const _ = require('underscore')
 const logger = require('@overleaf/logger')
 const { URL } = require('url')
-const { promisify, promisifyMultiResult } = require('../util/promises')
+const { promisify, promisifyMultiResult } = require('@overleaf/promise-utils')
 
 const DEFAULT_CURRENCY_CODE = 'USD'
 

+ 6 - 166
services/web/app/src/util/promises.js

@@ -1,168 +1,8 @@
-const { promisify, callbackify } = require('util')
-const pLimit = require('p-limit')
+// This stub module is temporary. Keep it around for a while to allow old
+// branches to be merged.
 
-module.exports = {
-  promisify,
-  promisifyAll,
-  promisifyClass,
-  promisifyMultiResult,
-  callbackify,
-  callbackifyMultiResult,
-  expressify,
-  promiseMapWithLimit,
-}
+module.exports = require('@overleaf/promise-utils')
 
-/**
- * Promisify all functions in a module.
- *
- * This is meant to be used only when all functions in the module are async
- * callback-style functions.
- *
- * It's very much tailored to our current module structure. In particular, it
- * binds `this` to the module when calling the function in order not to break
- * modules that call sibling functions using `this`.
- *
- * This will not magically fix all modules. Special cases should be promisified
- * manually.
- *
- * The second argument is a bag of options:
- *
- * - without: an array of function names that shouldn't be promisified
- *
- * - multiResult: an object whose keys are function names and values are lists
- *   of parameter names. This is meant for functions that invoke their callbacks
- *   with more than one result in separate parameters. The promisifed function
- *   will return these results as a single object, with each result keyed under
- *   the corresponding parameter name.
- */
-function promisifyAll(module, opts = {}) {
-  const { without = [], multiResult = {} } = opts
-  const promises = {}
-  for (const propName of Object.getOwnPropertyNames(module)) {
-    if (without.includes(propName)) {
-      continue
-    }
-    const propValue = module[propName]
-    if (typeof propValue !== 'function') {
-      continue
-    }
-    if (multiResult[propName] != null) {
-      promises[propName] = promisifyMultiResult(
-        propValue,
-        multiResult[propName]
-      ).bind(module)
-    } else {
-      promises[propName] = promisify(propValue).bind(module)
-    }
-  }
-  return promises
-}
-
-/**
- * Promisify all methods in a class.
- *
- * Options are the same as for promisifyAll
- */
-function promisifyClass(cls, opts = {}) {
-  const promisified = class extends cls {}
-  const { without = [], multiResult = {} } = opts
-  for (const propName of Object.getOwnPropertyNames(cls.prototype)) {
-    if (propName === 'constructor' || without.includes(propName)) {
-      continue
-    }
-    const propValue = cls.prototype[propName]
-    if (typeof propValue !== 'function') {
-      continue
-    }
-    if (multiResult[propName] != null) {
-      promisified.prototype[propName] = promisifyMultiResult(
-        propValue,
-        multiResult[propName]
-      )
-    } else {
-      promisified.prototype[propName] = promisify(propValue)
-    }
-  }
-  return promisified
-}
-
-/**
- * Promisify a function that returns multiple results via additional callback
- * parameters.
- *
- * The promisified function returns the results in a single object whose keys
- * are the names given in the array `resultNames`.
- *
- * Example:
- *
- *     function f(callback) {
- *       return callback(null, 1, 2, 3)
- *     }
- *
- *     const g = promisifyMultiResult(f, ['a', 'b', 'c'])
- *
- *     const result = await g()  // returns {a: 1, b: 2, c: 3}
- */
-function promisifyMultiResult(fn, resultNames) {
-  function promisified(...args) {
-    return new Promise((resolve, reject) => {
-      try {
-        fn.bind(this)(...args, (err, ...results) => {
-          if (err != null) {
-            return reject(err)
-          }
-          const promiseResult = {}
-          for (let i = 0; i < resultNames.length; i++) {
-            promiseResult[resultNames[i]] = results[i]
-          }
-          resolve(promiseResult)
-        })
-      } catch (err) {
-        reject(err)
-      }
-    })
-  }
-  return promisified
-}
-
-/**
- * Reverse the effect of `promisifyMultiResult`.
- *
- * This is meant for providing a temporary backward compatible callback
- * interface while we migrate to promises.
- */
-function callbackifyMultiResult(fn, resultNames) {
-  function callbackified(...args) {
-    const [callback] = args.splice(-1)
-    fn(...args)
-      .then(result => {
-        const cbResults = resultNames.map(resultName => result[resultName])
-        callback(null, ...cbResults)
-      })
-      .catch(err => {
-        callback(err)
-      })
-  }
-  return callbackified
-}
-
-/**
- * Transform an async function into an Express middleware
- *
- * Any error will be passed to the error middlewares via `next()`
- */
-function expressify(fn) {
-  return (req, res, next) => {
-    fn(req, res, next).catch(next)
-  }
-}
-
-/**
- * Map values in `array` with the async function `fn`
- *
- * Limit the number of unresolved promises to `concurrency`.
- */
-function promiseMapWithLimit(concurrency, array, fn) {
-  const limit = pLimit(concurrency)
-  return Promise.all(array.map(x => limit(() => fn(x))))
-}
+process.emitWarning(
+  'The util/promises module is deprecated. Use @overleaf/promise-utils instead.'
+)

+ 1 - 0
services/web/package.json

@@ -74,6 +74,7 @@
     "@overleaf/metrics": "*",
     "@overleaf/o-error": "*",
     "@overleaf/object-persistor": "*",
+    "@overleaf/promise-utils": "*",
     "@overleaf/redis-wrapper": "*",
     "@overleaf/settings": "*",
     "@slack/webhook": "^6.1.0",

+ 1 - 1
services/web/scripts/back_fill_deleted_files.js

@@ -1,5 +1,5 @@
 const { batchedUpdate } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit, promisify } = require('../app/src/util/promises')
+const { promiseMapWithLimit, promisify } = require('@overleaf/promise-utils')
 const { db } = require('../app/src/infrastructure/mongodb')
 const sleep = promisify(setTimeout)
 const _ = require('lodash')

+ 1 - 1
services/web/scripts/back_fill_doc_name_for_deleted_docs.js

@@ -1,5 +1,5 @@
 const { batchedUpdate } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit, promisify } = require('../app/src/util/promises')
+const { promiseMapWithLimit, promisify } = require('@overleaf/promise-utils')
 const { db } = require('../app/src/infrastructure/mongodb')
 const sleep = promisify(setTimeout)
 const _ = require('lodash')

+ 1 - 1
services/web/scripts/backfill_mixpanel_user_properties.js

@@ -3,7 +3,7 @@ const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
 require('../app/src/models/User')
 
 const { batchedUpdateWithResultHandling } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { getQueue } = require('../app/src/infrastructure/Queues')
 
 const SubscriptionLocator = require('../app/src/Features/Subscription/SubscriptionLocator')

+ 1 - 1
services/web/scripts/backfill_user_properties.js

@@ -1,7 +1,7 @@
 const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
 
 const { batchedUpdateWithResultHandling } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const SubscriptionLocator = require('../app/src/Features/Subscription/SubscriptionLocator')
 const PlansLocator = require('../app/src/Features/Subscription/PlansLocator')
 const FeaturesHelper = require('../app/src/Features/Subscription/FeaturesHelper')

+ 1 - 1
services/web/scripts/bench_bcrypt.js

@@ -1,7 +1,7 @@
 const minimist = require('minimist')
 const { promisify } = require('util')
 const bcrypt = require('bcrypt')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const csv = require('csv/sync')
 
 const bcryptCompare = promisify(bcrypt.compare)

+ 1 - 1
services/web/scripts/clear_sessions_2fa.js

@@ -1,4 +1,4 @@
-const { promisify, promiseMapWithLimit } = require('../app/src/util/promises')
+const { promisify, promiseMapWithLimit } = require('@overleaf/promise-utils')
 const rClient = require('../app/src/Features/User/UserSessionsRedis').client()
 
 const args = require('minimist')(process.argv.slice(2))

+ 1 - 1
services/web/scripts/convert_archived_state.js

@@ -4,7 +4,7 @@ const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10
 
 const { db } = require('../app/src/infrastructure/mongodb')
 const { batchedUpdate } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 
 // $ node scripts/convert_archived_state.js FIRST,SECOND
 

+ 1 - 1
services/web/scripts/delete_orphaned_chat_threads.js

@@ -14,7 +14,7 @@ process.env.MONGO_SOCKET_TIMEOUT =
   parseInt(process.env.MONGO_SOCKET_TIMEOUT, 10) || 600000
 
 const { ObjectId } = require('mongodb')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { batchedUpdate } = require('./helpers/batchedUpdate')
 const ChatApiHandler = require('../app/src/Features/Chat/ChatApiHandler')
 const { getHardDeletedProjectIds } = require('./delete_orphaned_data_helper')

+ 1 - 1
services/web/scripts/delete_orphaned_data_helper.js

@@ -3,7 +3,7 @@ const {
   READ_PREFERENCE_PRIMARY,
   READ_PREFERENCE_SECONDARY,
 } = require('../app/src/infrastructure/mongodb')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 
 async function getDeletedProject(projectId, readPreference) {
   return await db.deletedProjects.findOne(

+ 1 - 1
services/web/scripts/delete_orphaned_doc_comment_ranges.js

@@ -6,7 +6,7 @@ const { waitForDb } = require('../app/src/infrastructure/mongodb')
 const ChatApiHandler = require('../app/src/Features/Chat/ChatApiHandler')
 const DocstoreManager = require('../app/src/Features/Docstore/DocstoreManager')
 const DocumentUpdaterHandler = require('../app/src/Features/DocumentUpdater/DocumentUpdaterHandler')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 
 /**
  * Remove doc comment ranges that are "orphaned" as they do have matching chat

+ 1 - 1
services/web/scripts/delete_orphaned_docs_online_check.js

@@ -7,7 +7,7 @@ const {
   READ_PREFERENCE_PRIMARY,
   READ_PREFERENCE_SECONDARY,
 } = require('../app/src/infrastructure/mongodb')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { getHardDeletedProjectIds } = require('./delete_orphaned_data_helper')
 const sleep = promisify(setTimeout)
 

+ 1 - 1
services/web/scripts/delete_orphaned_project_archives.js

@@ -1,7 +1,7 @@
 const Settings = require('@overleaf/settings')
 const { fetchJson } = require('@overleaf/fetch-utils')
 const { waitForDb } = require('../app/src/infrastructure/mongodb')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const { getHardDeletedProjectIds } = require('./delete_orphaned_data_helper')
 const TpdsUpdateSender = require('../app/src/Features/ThirdPartyDataStore/TpdsUpdateSender')
 const { promisify } = require('util')

+ 1 - 1
services/web/scripts/migrate_audit_logs.js

@@ -1,5 +1,5 @@
 const { batchedUpdate } = require('./helpers/batchedUpdate')
-const { promiseMapWithLimit, promisify } = require('../app/src/util/promises')
+const { promiseMapWithLimit, promisify } = require('@overleaf/promise-utils')
 const { db, ObjectId, waitForDb } = require('../app/src/infrastructure/mongodb')
 const sleep = promisify(setTimeout)
 const _ = require('lodash')

+ 1 - 1
services/web/scripts/regenerate_duplicate_referral_ids.js

@@ -8,7 +8,7 @@ const {
   db,
   READ_PREFERENCE_SECONDARY,
 } = require('../app/src/infrastructure/mongodb')
-const { promiseMapWithLimit } = require('../app/src/util/promises')
+const { promiseMapWithLimit } = require('@overleaf/promise-utils')
 const TokenGenerator = require('../app/src/Features/TokenGenerator/TokenGenerator')
 const { batchedUpdate } = require('./helpers/batchedUpdate')
 

+ 1 - 1
services/web/test/acceptance/src/helpers/RecurlySubscription.js

@@ -2,7 +2,7 @@ const { ObjectId } = require('mongodb')
 const Subscription = require('./Subscription')
 const MockRecurlyApiClass = require('../mocks/MockRecurlyApi')
 const RecurlyWrapper = require('../../../../app/src/Features/Subscription/RecurlyWrapper')
-const { promisifyClass } = require('../../../../app/src/util/promises')
+const { promisifyClass } = require('@overleaf/promise-utils')
 
 let MockRecurlyApi
 

+ 0 - 1
services/web/test/unit/bootstrap.js

@@ -64,7 +64,6 @@ function getSandboxedModuleRequires() {
   }
 
   const internalModules = [
-    '../../app/src/util/promises',
     '../../app/src/Features/Errors/Errors',
     '../../app/src/Features/Helpers/Mongo',
   ]

+ 0 - 211
services/web/test/unit/src/util/promisesTests.js

@@ -1,211 +0,0 @@
-const { expect } = require('chai')
-const {
-  promisifyAll,
-  promisifyClass,
-  callbackifyMultiResult,
-} = require('../../../../app/src/util/promises')
-
-describe('promisifyAll', function () {
-  describe('basic functionality', function () {
-    before(function () {
-      this.module = {
-        SOME_CONSTANT: 1,
-        asyncAdd(a, b, callback) {
-          callback(null, a + b)
-        },
-        asyncDouble(x, callback) {
-          this.asyncAdd(x, x, callback)
-        },
-      }
-      this.promisified = promisifyAll(this.module)
-    })
-
-    it('promisifies functions in the module', async function () {
-      const sum = await this.promisified.asyncAdd(29, 33)
-      expect(sum).to.equal(62)
-    })
-
-    it('binds this to the original module', async function () {
-      const sum = await this.promisified.asyncDouble(38)
-      expect(sum).to.equal(76)
-    })
-
-    it('does not copy over non-functions', async function () {
-      expect(this.promisified).not.to.have.property('SOME_CONSTANT')
-    })
-
-    it('does not modify the prototype of the module', async function () {
-      expect(this.promisified.toString()).to.equal('[object Object]')
-    })
-  })
-
-  describe('without option', function () {
-    before(function () {
-      this.module = {
-        asyncAdd(a, b, callback) {
-          callback(null, a + b)
-        },
-        syncAdd(a, b) {
-          return a + b
-        },
-      }
-      this.promisified = promisifyAll(this.module, { without: ['syncAdd'] })
-    })
-
-    it('does not promisify excluded functions', function () {
-      expect(this.promisified.syncAdd).not.to.exist
-    })
-
-    it('promisifies other functions', async function () {
-      const sum = await this.promisified.asyncAdd(12, 89)
-      expect(sum).to.equal(101)
-    })
-  })
-
-  describe('multiResult option', function () {
-    before(function () {
-      this.module = {
-        asyncAdd(a, b, callback) {
-          callback(null, a + b)
-        },
-        asyncArithmetic(a, b, callback) {
-          callback(null, a + b, a * b)
-        },
-      }
-      this.promisified = promisifyAll(this.module, {
-        multiResult: { asyncArithmetic: ['sum', 'product'] },
-      })
-    })
-
-    it('promisifies multi-result functions', async function () {
-      const result = await this.promisified.asyncArithmetic(3, 6)
-      expect(result).to.deep.equal({ sum: 9, product: 18 })
-    })
-
-    it('promisifies other functions normally', async function () {
-      const sum = await this.promisified.asyncAdd(6, 1)
-      expect(sum).to.equal(7)
-    })
-  })
-})
-
-describe('promisifyClass', function () {
-  describe('basic functionality', function () {
-    before(function () {
-      this.Class = class {
-        constructor(a) {
-          this.a = a
-        }
-
-        asyncAdd(b, callback) {
-          callback(null, this.a + b)
-        }
-      }
-      this.Promisified = promisifyClass(this.Class)
-    })
-
-    it('promisifies the class methods', async function () {
-      const adder = new this.Promisified(1)
-      const sum = await adder.asyncAdd(2)
-      expect(sum).to.equal(3)
-    })
-  })
-
-  describe('without option', function () {
-    before(function () {
-      this.Class = class {
-        constructor(a) {
-          this.a = a
-        }
-
-        asyncAdd(b, callback) {
-          callback(null, this.a + b)
-        }
-
-        syncAdd(b) {
-          return this.a + b
-        }
-      }
-      this.Promisified = promisifyClass(this.Class, { without: ['syncAdd'] })
-    })
-
-    it('does not promisify excluded functions', function () {
-      const adder = new this.Promisified(10)
-      const sum = adder.syncAdd(12)
-      expect(sum).to.equal(22)
-    })
-
-    it('promisifies other functions', async function () {
-      const adder = new this.Promisified(23)
-      const sum = await adder.asyncAdd(3)
-      expect(sum).to.equal(26)
-    })
-  })
-
-  describe('multiResult option', function () {
-    before(function () {
-      this.Class = class {
-        constructor(a) {
-          this.a = a
-        }
-
-        asyncAdd(b, callback) {
-          callback(null, this.a + b)
-        }
-
-        asyncArithmetic(b, callback) {
-          callback(null, this.a + b, this.a * b)
-        }
-      }
-      this.Promisified = promisifyClass(this.Class, {
-        multiResult: { asyncArithmetic: ['sum', 'product'] },
-      })
-    })
-
-    it('promisifies multi-result functions', async function () {
-      const adder = new this.Promisified(3)
-      const result = await adder.asyncArithmetic(6)
-      expect(result).to.deep.equal({ sum: 9, product: 18 })
-    })
-
-    it('promisifies other functions normally', async function () {
-      const adder = new this.Promisified(6)
-      const sum = await adder.asyncAdd(1)
-      expect(sum).to.equal(7)
-    })
-  })
-})
-
-describe('callbackifyMultiResult', function () {
-  it('callbackifies a multi-result function', function (done) {
-    async function asyncArithmetic(a, b) {
-      return { sum: a + b, product: a * b }
-    }
-    const callbackified = callbackifyMultiResult(asyncArithmetic, [
-      'sum',
-      'product',
-    ])
-    callbackified(3, 11, (err, sum, product) => {
-      if (err != null) {
-        return done(err)
-      }
-      expect(sum).to.equal(14)
-      expect(product).to.equal(33)
-      done()
-    })
-  })
-
-  it('propagates errors', function (done) {
-    async function asyncBomb() {
-      throw new Error('BOOM!')
-    }
-    const callbackified = callbackifyMultiResult(asyncBomb, [
-      'explosives',
-      'dynamite',
-    ])
-    callbackified(err => {
-      expect(err).to.exist
-      done()
-    })
-  })
-})