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

Merge pull request #14980 from overleaf/jpa-debug-console

[web] migrate frontend logging to debugConsole

GitOrigin-RevId: b03ff9030b03fff059738d2270bc4b4912e79f16
Jakob Ackermann 3 лет назад
Родитель
Сommit
1213709578
100 измененных файлов с 340 добавлено и 242 удалено
  1. 22 5
      services/web/.eslintrc
  2. 3 2
      services/web/docker-compose.cypress.yml
  3. 3 0
      services/web/docker-compose.yml
  4. 0 4
      services/web/frontend/js/base.js
  5. 3 6
      services/web/frontend/js/features/chat/components/message-content.tsx
  6. 2 1
      services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx
  7. 3 2
      services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings.tsx
  8. 2 1
      services/web/frontend/js/features/editor-left-menu/utils/api.ts
  9. 2 1
      services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.js
  10. 2 3
      services/web/frontend/js/features/file-view/components/file-view-header.js
  11. 2 1
      services/web/frontend/js/features/file-view/components/file-view-text.js
  12. 2 1
      services/web/frontend/js/features/form-helpers/captcha.js
  13. 2 1
      services/web/frontend/js/features/group-management/components/managed-users/offboard-managed-user-modal.tsx
  14. 3 2
      services/web/frontend/js/features/group-management/components/managers-table.tsx
  15. 3 2
      services/web/frontend/js/features/group-management/context/group-members-context.tsx
  16. 2 1
      services/web/frontend/js/features/history/components/change-list/add-label-modal.tsx
  17. 2 1
      services/web/frontend/js/features/history/components/change-list/all-history-list.tsx
  18. 2 1
      services/web/frontend/js/features/history/components/change-list/tag-tooltip.tsx
  19. 3 1
      services/web/frontend/js/features/history/utils/wait-for.ts
  20. 3 2
      services/web/frontend/js/features/pdf-preview/components/pdf-js-viewer.js
  21. 3 6
      services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.js
  22. 4 3
      services/web/frontend/js/features/pdf-preview/util/compiler.js
  23. 2 1
      services/web/frontend/js/features/pdf-preview/util/metrics.js
  24. 3 2
      services/web/frontend/js/features/pdf-preview/util/output-files.js
  25. 3 2
      services/web/frontend/js/features/pdf-preview/util/pdf-caching-flags.js
  26. 3 2
      services/web/frontend/js/features/pdf-preview/util/pdf-caching-transport.js
  27. 2 1
      services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.js
  28. 2 1
      services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx
  29. 2 1
      services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx
  30. 2 1
      services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx
  31. 3 2
      services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx
  32. 2 1
      services/web/frontend/js/features/project-list/components/modals/rename-project-modal.tsx
  33. 2 1
      services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx
  34. 2 1
      services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx
  35. 2 1
      services/web/frontend/js/features/project-list/components/notifications/groups/confirm-email.tsx
  36. 3 3
      services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/hooks/use-group-invitation-notification.tsx
  37. 2 1
      services/web/frontend/js/features/project-list/components/notifications/hooks/useAsyncDismiss.ts
  38. 2 1
      services/web/frontend/js/features/project-list/components/notifications/system-messages.tsx
  39. 2 1
      services/web/frontend/js/features/project-list/context/project-list-context.tsx
  40. 2 1
      services/web/frontend/js/features/settings/components/emails/add-email/input.tsx
  41. 2 1
      services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx
  42. 3 2
      services/web/frontend/js/features/share-project-modal/components/link-sharing.js
  43. 5 4
      services/web/frontend/js/features/share-project-modal/controllers/react-share-project-modal-controller.js
  44. 4 1
      services/web/frontend/js/features/source-editor/components/table-generator/contexts/editing-context.tsx
  45. 3 2
      services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts
  46. 2 1
      services/web/frontend/js/features/source-editor/extensions/annotations.ts
  47. 2 1
      services/web/frontend/js/features/source-editor/extensions/cursor-highlights.ts
  48. 4 3
      services/web/frontend/js/features/source-editor/extensions/cursor-position.ts
  49. 2 1
      services/web/frontend/js/features/source-editor/extensions/figure-modal.ts
  50. 5 4
      services/web/frontend/js/features/source-editor/extensions/realtime.ts
  51. 2 1
      services/web/frontend/js/features/source-editor/extensions/scroll-position.ts
  52. 4 3
      services/web/frontend/js/features/source-editor/extensions/spelling/context-menu.ts
  53. 2 5
      services/web/frontend/js/features/source-editor/extensions/spelling/spellchecker.ts
  54. 2 1
      services/web/frontend/js/features/source-editor/extensions/track-changes.ts
  55. 2 1
      services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts
  56. 2 3
      services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/graphics.ts
  57. 2 1
      services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/tabular.tsx
  58. 2 3
      services/web/frontend/js/features/source-editor/extensions/visual/visual.ts
  59. 2 1
      services/web/frontend/js/features/source-editor/languages/latex/debug-print.ts
  60. 2 1
      services/web/frontend/js/features/source-editor/languages/latex/latex-indent-service.ts
  61. 2 1
      services/web/frontend/js/features/source-editor/lezer-latex/print-tree.mjs
  62. 2 2
      services/web/frontend/js/features/source-editor/themes/scripts/convert.js
  63. 0 0
      services/web/frontend/js/features/source-editor/themes/scripts/overrides.json
  64. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/leave-group-modal.tsx
  65. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/managed-institution.tsx
  66. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/reactivate-subscription.tsx
  67. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx
  68. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/downgrade-plan-button.tsx
  69. 2 1
      services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/extend-trial-button.tsx
  70. 4 3
      services/web/frontend/js/features/subscription/context/subscription-dashboard-context.tsx
  71. 2 3
      services/web/frontend/js/features/subscription/util/recurly-pricing.ts
  72. 3 2
      services/web/frontend/js/ide.js
  73. 32 31
      services/web/frontend/js/ide/connection/ConnectionManager.js
  74. 3 1
      services/web/frontend/js/ide/connection/EditorWatchdogManager.js
  75. 5 3
      services/web/frontend/js/ide/connection/SocketIoShim.js
  76. 24 23
      services/web/frontend/js/ide/editor/Document.js
  77. 10 9
      services/web/frontend/js/ide/editor/EditorManager.js
  78. 15 12
      services/web/frontend/js/ide/editor/ShareJsDoc.js
  79. 2 1
      services/web/frontend/js/ide/editor/ace-performance.ts
  80. 2 1
      services/web/frontend/js/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.js
  81. 3 2
      services/web/frontend/js/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.js
  82. 2 1
      services/web/frontend/js/ide/file-tree/FileTreeManager.js
  83. 2 1
      services/web/frontend/js/ide/history/controllers/HistoryV2ToolbarController.js
  84. 0 1
      services/web/frontend/js/ide/references/ReferencesManager.js
  85. 4 4
      services/web/frontend/js/ide/review-panel/directives/reviewPanelSorted.js
  86. 2 1
      services/web/frontend/js/ide/services/ide.js
  87. 2 1
      services/web/frontend/js/infrastructure/cm6-performance.ts
  88. 4 3
      services/web/frontend/js/infrastructure/error-reporter.js
  89. 3 1
      services/web/frontend/js/infrastructure/local-storage.js
  90. 3 1
      services/web/frontend/js/infrastructure/session-storage.js
  91. 2 1
      services/web/frontend/js/main.js
  92. 4 1
      services/web/frontend/js/main/annual-upgrade.js
  93. 0 1
      services/web/frontend/js/main/clear-sessions.js
  94. 3 2
      services/web/frontend/js/main/event.js
  95. 3 2
      services/web/frontend/js/main/subscription/team-invite-controller.js
  96. 4 3
      services/web/frontend/js/main/token-access.js
  97. 3 1
      services/web/frontend/js/modules/localStorage.js
  98. 3 1
      services/web/frontend/js/modules/sessionStorage.js
  99. 5 4
      services/web/frontend/js/shared/context/detach-context.js
  100. 2 1
      services/web/frontend/js/shared/context/layout-context.js

+ 22 - 5
services/web/.eslintrc

@@ -94,9 +94,6 @@
       // Backend specific rules
       "files": ["**/app/src/**/*.js", "app.js"],
       "rules": {
-        // don't allow console.log in backend code
-        "no-console": "error",
-
         // do not allow importing of implicit dependencies.
         "import/no-extraneous-dependencies": ["error", {
           // do not allow importing of devDependencies.
@@ -179,8 +176,6 @@
         "angular": true,
         "ace": true,
         "ga": true,
-        "sl_console": true,
-        "sl_debugging": true,
         // Injected in layout.pug
         "user_id": true,
         "ExposedSettings": true
@@ -274,6 +269,28 @@
           "packageDir": [".", "../../"]
         }]
       }
+    },
+    {
+      "files": [
+        // Backend: Use @overleaf/logger
+        //          Docs: https://manual.dev-overleaf.com/development/code/logging/#structured-logging
+        "**/app/**/*.{js,cjs,mjs}", "app.js", "modules/*/*.js",
+        // Frontend: Prefer debugConsole over bare console
+        //           Docs: https://manual.dev-overleaf.com/development/code/logging/#frontend
+        "**/frontend/**/*.{js,ts,tsx}"
+      ],
+      "excludedFiles": [
+        // Allow console logs in scripts
+        "**/scripts/**/*.js",
+        // Allow console logs in stories
+        "**/stories/**/*.{js,ts,tsx}",
+        // Workers do not have access to the search params for enabling ?debug=true.
+        // self.location.url is the URL of the worker script.
+        "*.worker.{js,ts}"
+      ],
+      "rules": {
+        "no-console": "error"
+      }
     }
   ]
 }

+ 3 - 2
services/web/docker-compose.cypress.yml

@@ -8,5 +8,6 @@ services:
     entrypoint: npm
     user: "${DOCKER_USER:-1000:1000}"
     environment:
-      - "DISPLAY=${DISPLAY:-:0}"
-#      - "DEBUG=cypress.*"
+      VERBOSE_LOGGING:
+      DISPLAY: ${DISPLAY:-:0}
+#      DEBUG: cypress.*

+ 3 - 0
services/web/docker-compose.yml

@@ -72,6 +72,7 @@ services:
     environment:
       MOCHA_GREP: ${MOCHA_GREP}
       NODE_OPTIONS: "--unhandled-rejections=strict"
+      VERBOSE_LOGGING:
     command: npm run --silent test:frontend
     user: node
 
@@ -80,6 +81,8 @@ services:
     volumes:
       - ../../:/overleaf
     working_dir: /overleaf/services/web
+    environment:
+      VERBOSE_LOGGING:
     entrypoint: npm
     command:
       - "run"

+ 0 - 4
services/web/frontend/js/base.js

@@ -56,8 +56,4 @@ App.run(($rootScope, $templateCache) => {
   )
 })
 
-const sl_debugging = window.location.search.match(/debug=true/)
-window.sl_debugging = sl_debugging // make a global flag for debugging code
-window.sl_console = sl_debugging ? console : { log() {} }
-
 export default App

+ 3 - 6
services/web/frontend/js/features/chat/components/message-content.tsx

@@ -4,6 +4,7 @@ import Linkify from 'react-linkify'
 import useIsMounted from '../../../shared/hooks/use-is-mounted'
 import { configureMathJax } from '../../mathjax/configure'
 import { loadMathJax } from '../../mathjax/load-mathjax'
+import { debugConsole } from '@/utils/debugging'
 
 const MessageContent: FC<{ content: string }> = ({ content }) => {
   const root = useRef<HTMLDivElement | null>(null)
@@ -33,14 +34,10 @@ const MessageContent: FC<{ content: string }> = ({ content }) => {
       loadMathJax()
         .then(MathJax => {
           if (mounted.current) {
-            MathJax.typesetPromise([root.current]).catch((error: Error) => {
-              console.error(error)
-            })
+            MathJax.typesetPromise([root.current]).catch(debugConsole.error)
           }
         })
-        .catch(error => {
-          console.error(error)
-        })
+        .catch(debugConsole.error)
     }
   }, [content, mounted])
 

+ 2 - 1
services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx

@@ -6,6 +6,7 @@ import Tooltip from '../../../shared/components/tooltip'
 import useAsync from '../../../shared/hooks/use-async'
 import { postJSON } from '../../../infrastructure/fetch-json'
 import ignoredWords from '../ignored-words'
+import { debugConsole } from '@/utils/debugging'
 
 type DictionaryModalContentProps = {
   handleHide: () => void
@@ -29,7 +30,7 @@ export default function DictionaryModalContent({
             word,
           },
         })
-      ).catch(console.error)
+      ).catch(debugConsole.error)
     },
     [runAsync]
   )

+ 3 - 2
services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings.tsx

@@ -4,6 +4,7 @@ import type { ProjectSettings } from '../utils/api'
 import useRootDocId from './use-root-doc-id'
 import useSaveProjectSettings from './use-save-project-settings'
 import useSetSpellCheckLanguage from './use-set-spell-check-language'
+import { debugConsole } from '@/utils/debugging'
 
 export default function useProjectWideSettings() {
   // The value will be undefined on mount
@@ -12,14 +13,14 @@ export default function useProjectWideSettings() {
 
   const setCompiler = useCallback(
     (newCompiler: ProjectSettings['compiler']) => {
-      saveProjectSettings('compiler', newCompiler).catch(console.error)
+      saveProjectSettings('compiler', newCompiler).catch(debugConsole.error)
     },
     [saveProjectSettings]
   )
 
   const setImageName = useCallback(
     (newImageName: ProjectSettings['imageName']) => {
-      saveProjectSettings('imageName', newImageName).catch(console.error)
+      saveProjectSettings('imageName', newImageName).catch(debugConsole.error)
     },
     [saveProjectSettings]
   )

+ 2 - 1
services/web/frontend/js/features/editor-left-menu/utils/api.ts

@@ -10,6 +10,7 @@ import type {
 } from '../../../../../types/project-settings'
 import { sendMB } from '../../../infrastructure/event-tracking'
 import { postJSON } from '../../../infrastructure/fetch-json'
+import { debugConsole } from '@/utils/debugging'
 
 export type UserSettings = {
   pdfViewer: PdfViewer
@@ -50,7 +51,7 @@ export function saveUserSettings(
     body: {
       [key]: value,
     },
-  }).catch(console.error)
+  }).catch(debugConsole.error)
 }
 
 export const saveProjectSettings = async (

+ 2 - 1
services/web/frontend/js/features/file-tree/components/file-tree-create/modes/file-tree-upload-doc.js

@@ -13,6 +13,7 @@ import '@uppy/core/dist/style.css'
 import '@uppy/dashboard/dist/style.css'
 import { refreshProjectMetadata } from '../../../util/api'
 import ErrorMessage from '../error-message'
+import { debugConsole } from '@/utils/debugging'
 
 export default function FileTreeUploadDoc() {
   const { parentFolderId, cancel, isDuplicate, droppedFiles, setDroppedFiles } =
@@ -114,7 +115,7 @@ export default function FileTreeUploadDoc() {
               break
 
             default:
-              console.error(error)
+              debugConsole.error(error)
               setError(response?.body?.error || 'generic_something_went_wrong')
               break
           }

+ 2 - 3
services/web/frontend/js/features/file-view/components/file-view-header.js

@@ -11,6 +11,7 @@ import { useProjectContext } from '../../../shared/context/project-context'
 import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
 import useAbortController from '../../../shared/hooks/use-abort-controller'
 import { LinkedFileIcon } from './file-view-icons'
+import { debugConsole } from '@/utils/debugging'
 const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo')
 const tprLinkedFileRefreshError = importOverleafModules(
   'tprLinkedFileRefreshError'
@@ -102,9 +103,7 @@ export default function FileViewHeader({ file, storeReferencesKeys }) {
           // Later updated by the socket but also updated here for immediate use
           storeReferencesKeys(response.keys)
         })
-        .catch(error => {
-          console.log(error)
-        })
+        .catch(debugConsole.error)
     }
   }, [file, projectId, signal, storeReferencesKeys])
 

+ 2 - 1
services/web/frontend/js/features/file-view/components/file-view-text.js

@@ -1,6 +1,7 @@
 import { useState, useEffect } from 'react'
 import PropTypes from 'prop-types'
 import { useProjectContext } from '../../../shared/context/project-context'
+import { debugConsole } from '@/utils/debugging'
 
 const MAX_FILE_SIZE = 2 * 1024 * 1024
 
@@ -47,7 +48,7 @@ export default function FileViewText({ file, onLoad, onError }) {
         })
       })
       .catch(err => {
-        console.error(err)
+        debugConsole.error(err)
         onError()
       })
       .finally(() => {

+ 2 - 1
services/web/frontend/js/features/form-helpers/captcha.js

@@ -1,5 +1,6 @@
 import 'abort-controller/polyfill'
 import { postJSON } from '../../infrastructure/fetch-json'
+import { debugConsole } from '@/utils/debugging'
 
 const grecaptcha = window.grecaptcha
 
@@ -50,7 +51,7 @@ function emitError(err, src) {
     `captcha check failed: ${getMessage(err)}, please retry again`
   )
   // Keep a record of this error. 2nd line might request a screenshot of it.
-  console.error(err, src)
+  debugConsole.error(err, src)
 
   recaptchaCallbacks.splice(0).forEach(({ reject, resetTimeout }) => {
     resetTimeout()

+ 2 - 1
services/web/frontend/js/features/group-management/components/managed-users/offboard-managed-user-modal.tsx

@@ -15,6 +15,7 @@ import useAsync from '../../../../shared/hooks/use-async'
 import { useTranslation } from 'react-i18next'
 import { useLocation } from '../../../../shared/hooks/use-location'
 import { FetchError, postJSON } from '../../../../infrastructure/fetch-json'
+import { debugConsole } from '@/utils/debugging'
 
 type OffboardManagedUserModalProps = {
   user: User
@@ -59,7 +60,7 @@ export default function OffboardManagedUserModal({
           location.reload()
         })
         .catch(err => {
-          console.error(err)
+          debugConsole.error(err)
           setError(
             err instanceof FetchError ? err.getUserFacingMessage() : err.message
           )

+ 3 - 2
services/web/frontend/js/features/group-management/components/managers-table.tsx

@@ -14,6 +14,7 @@ import ErrorAlert, { APIError } from './error-alert'
 import GroupMemberRow from './group-member-row'
 import useUserSelection from '../hooks/use-user-selection'
 import { User } from '../../../../../types/group-management/user'
+import { debugConsole } from '@/utils/debugging'
 
 type ManagersPaths = {
   addMember: string
@@ -77,7 +78,7 @@ export function ManagersTable({
             }
             setEmailString('')
           } catch (error: unknown) {
-            console.error(error)
+            debugConsole.error(error)
             setInviteError((error as FetchError)?.data?.error || {})
           }
           setInviteUserInflightCount(count => count - 1)
@@ -105,7 +106,7 @@ export function ManagersTable({
             setUsers(users => users.filter(u => u !== user))
             unselectUser(user)
           } catch (error: unknown) {
-            console.error(error)
+            debugConsole.error(error)
             setRemoveMemberError((error as FetchError)?.data?.error || {})
           }
           setRemoveMemberInflightCount(count => count - 1)

+ 3 - 2
services/web/frontend/js/features/group-management/context/group-members-context.tsx

@@ -17,6 +17,7 @@ import getMeta from '../../../utils/meta'
 import { APIError } from '../components/error-alert'
 import useUserSelection from '../hooks/use-user-selection'
 import { parseEmails } from '../utils/emails'
+import { debugConsole } from '@/utils/debugging'
 
 export type GroupMembersContextValue = {
   users: User[]
@@ -94,7 +95,7 @@ export function GroupMembersProvider({ children }: GroupMembersProviderProps) {
             }
           }
         } catch (error: unknown) {
-          console.error(error)
+          debugConsole.error(error)
           setInviteError((error as FetchError)?.data?.error || {})
         }
         setInviteUserInflightCount(count => count - 1)
@@ -119,7 +120,7 @@ export function GroupMembersProvider({ children }: GroupMembersProviderProps) {
         setUsers(users => users.filter(u => u !== user))
         unselectUser(user)
       } catch (error: unknown) {
-        console.error(error)
+        debugConsole.error(error)
         setRemoveMemberError((error as FetchError)?.data?.error || {})
       }
       setRemoveMemberInflightCount(count => count - 1)

+ 2 - 1
services/web/frontend/js/features/history/components/change-list/add-label-modal.tsx

@@ -10,6 +10,7 @@ import { useHistoryContext } from '../../context/history-context'
 import { addLabel } from '../../services/api'
 import { Label } from '../../services/types/label'
 import { useRefWithAutoFocus } from '../../../../shared/hooks/use-ref-with-auto-focus'
+import { debugConsole } from '@/utils/debugging'
 
 type AddLabelModalProps = {
   show: boolean
@@ -59,7 +60,7 @@ function AddLabelModal({ show, setShow, version }: AddLabelModalProps) {
 
     runAsync(addLabel(projectId, { comment, version }, signal))
       .then(() => setShow(false))
-      .catch(console.error)
+      .catch(debugConsole.error)
   }
 
   const responseError = error as unknown as {

+ 2 - 1
services/web/frontend/js/features/history/components/change-list/all-history-list.tsx

@@ -14,6 +14,7 @@ import { Trans, useTranslation } from 'react-i18next'
 import MaterialIcon from '@/shared/components/material-icon'
 import useAsync from '@/shared/hooks/use-async'
 import { completeHistoryTutorial } from '../../services/api'
+import { debugConsole } from '@/utils/debugging'
 
 type CompletedTutorials = {
   'react-history-buttons-tutorial': Date
@@ -135,7 +136,7 @@ function AllHistoryList() {
   ) {
     const dismissModal = () => {
       completeTutorial()
-      runAsync(completeHistoryTutorial()).catch(console.error)
+      runAsync(completeHistoryTutorial()).catch(debugConsole.error)
     }
 
     popover = (

+ 2 - 1
services/web/frontend/js/features/history/components/change-list/tag-tooltip.tsx

@@ -14,6 +14,7 @@ import { deleteLabel } from '../../services/api'
 import { isPseudoLabel } from '../../utils/label'
 import { formatDate } from '../../../../utils/dates'
 import { LoadedLabel } from '../../services/types/label'
+import { debugConsole } from '@/utils/debugging'
 
 type TagProps = {
   label: LoadedLabel
@@ -50,7 +51,7 @@ function Tag({ label, currentUserId, ...props }: TagProps) {
   const localDeleteHandler = () => {
     runAsync(deleteLabel(projectId, label.id, signal))
       .then(() => setShowDeleteModal(false))
-      .catch(console.error)
+      .catch(debugConsole.error)
   }
 
   const responseError = error as unknown as {

+ 3 - 1
services/web/frontend/js/features/history/utils/wait-for.ts

@@ -1,3 +1,5 @@
+import { debugConsole } from '@/utils/debugging'
+
 export function waitFor<T>(
   testFunction: () => T,
   timeout: number,
@@ -12,7 +14,7 @@ export function waitFor<T>(
         const err = new Error(
           `waiting too long, ${JSON.stringify({ timeout, pollInterval })}`
         )
-        console.error(err)
+        debugConsole.error(err)
         reject(err)
         return
       }

+ 3 - 2
services/web/frontend/js/features/pdf-preview/components/pdf-js-viewer.js

@@ -12,6 +12,7 @@ import { useDetachCompileContext as useCompileContext } from '../../../shared/co
 import { captureException } from '../../../infrastructure/error-reporter'
 import * as eventTracking from '../../../infrastructure/event-tracking'
 import { getPdfCachingMetrics } from '../util/metrics'
+import { debugConsole } from '@/utils/debugging'
 
 function PdfJsViewer({ url, pdfFile }) {
   const { _id: projectId } = useProjectContext()
@@ -136,7 +137,7 @@ function PdfJsViewer({ url, pdfFile }) {
         .loadDocument({ url, pdfFile, abortController, handleFetchError })
         .catch(error => {
           if (abortController.signal.aborted) return
-          console.error(error)
+          debugConsole.error(error)
           setError('rendering-error')
         })
       return () => {
@@ -161,7 +162,7 @@ function PdfJsViewer({ url, pdfFile }) {
         try {
           setPosition(pdfViewer.currentPosition)
         } catch (error) {
-          // console.error(error)
+          // debugConsole.error(error)
         }
       }, 500)
 

+ 3 - 6
services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.js

@@ -19,6 +19,7 @@ import localStorage from '../../../infrastructure/local-storage'
 import { useFileTreeData } from '../../../shared/context/file-tree-data-context'
 import useScopeEventListener from '../../../shared/hooks/use-scope-event-listener'
 import * as eventTracking from '../../../infrastructure/event-tracking'
+import { debugConsole } from '@/utils/debugging'
 
 function GoToCodeButton({
   position,
@@ -209,9 +210,7 @@ function PdfSynctexControls() {
           setShowLogs(false)
           setHighlights(data.pdf)
         })
-        .catch(error => {
-          console.error(error)
-        })
+        .catch(debugConsole.error)
         .finally(() => {
           if (isMounted.current) {
             setSyncToPdfInFlight(false)
@@ -297,9 +296,7 @@ function PdfSynctexControls() {
           const [{ file, line }] = data.code
           goToCodeLine(file, line)
         })
-        .catch(error => {
-          console.error(error)
-        })
+        .catch(debugConsole.error)
         .finally(() => {
           if (isMounted.current) {
             setSyncToCodeInFlight(false)

+ 4 - 3
services/web/frontend/js/features/pdf-preview/util/compiler.js

@@ -4,6 +4,7 @@ import { deleteJSON, postJSON } from '../../../infrastructure/fetch-json'
 import { debounce } from 'lodash'
 import { trackPdfDownload } from './metrics'
 import { enablePdfCaching } from './pdf-caching-flags'
+import { debugConsole } from '@/utils/debugging'
 
 const AUTO_COMPILE_MAX_WAIT = 5000
 // We add a 2 second debounce to sending user changes to server if they aren't
@@ -127,7 +128,7 @@ export default class DocumentCompiler {
       }
       this.setData(data)
     } catch (error) {
-      console.error(error)
+      debugConsole.error(error)
       this.cleanupCompileResult()
       this.setError(error.info?.statusCode === 429 ? 'rate-limited' : 'error')
     } finally {
@@ -206,7 +207,7 @@ export default class DocumentCompiler {
       signal: this.signal,
     })
       .catch(error => {
-        console.error(error)
+        debugConsole.error(error)
         this.setError('error')
       })
       .finally(() => {
@@ -221,7 +222,7 @@ export default class DocumentCompiler {
     return deleteJSON(`/project/${this.projectId}/output?${params}`, {
       signal: this.signal,
     }).catch(error => {
-      console.error(error)
+      debugConsole.error(error)
       this.setError('clear-cache')
     })
   }

+ 2 - 1
services/web/frontend/js/features/pdf-preview/util/metrics.js

@@ -1,6 +1,7 @@
 import { v4 as uuid } from 'uuid'
 import { sendMB } from '../../../infrastructure/event-tracking'
 import { trackPdfDownloadEnabled } from './pdf-caching-flags'
+import { debugConsole } from '@/utils/debugging'
 
 // VERSION should get incremented when making changes to caching behavior or
 //  adjusting metrics collection.
@@ -60,6 +61,6 @@ function submitCompileMetrics(metrics) {
     ...metrics,
     id: EDITOR_SESSION_ID,
   }
-  sl_console.log('/event/compile-metrics', JSON.stringify(leanMetrics))
+  debugConsole.log('/event/compile-metrics', JSON.stringify(leanMetrics))
   sendMB('compile-metrics-v6', leanMetrics)
 }

+ 3 - 2
services/web/frontend/js/features/pdf-preview/util/output-files.js

@@ -3,6 +3,7 @@ import HumanReadableLogs from '../../../ide/human-readable-logs/HumanReadableLog
 import BibLogParser from '../../../ide/log-parser/bib-log-parser'
 import { v4 as uuid } from 'uuid'
 import { enablePdfCaching } from './pdf-caching-flags'
+import { debugConsole } from '@/utils/debugging'
 
 // Warnings that may disappear after a second LaTeX pass
 const TRANSIENT_WARNING_REGEX = /^(Reference|Citation).+undefined on input line/
@@ -91,7 +92,7 @@ export const handleLogFiles = async (outputFiles, data, signal) => {
 
       accumulateResults({ errors, warnings, typesetting })
     } catch (e) {
-      console.warn(e) // ignore failure to fetch/parse the log file, but log a warning
+      debugConsole.warn(e) // ignore failure to fetch/parse the log file, but log a warning
     }
   }
 
@@ -119,7 +120,7 @@ export const handleLogFiles = async (outputFiles, data, signal) => {
         // BibLog parsing errors are ignored
       }
     } catch (e) {
-      console.warn(e) // ignore failure to fetch/parse the log file, but log a warning
+      debugConsole.warn(e) // ignore failure to fetch/parse the log file, but log a warning
     }
   }
 

+ 3 - 2
services/web/frontend/js/features/pdf-preview/util/pdf-caching-flags.js

@@ -1,15 +1,16 @@
 import getMeta from '../../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 const hasTextEncoder = typeof TextEncoder !== 'undefined'
 if (!hasTextEncoder) {
-  console.warn('TextEncoder is not available. Disabling pdf-caching.')
+  debugConsole.warn('TextEncoder is not available. Disabling pdf-caching.')
 }
 
 const isOpera =
   Array.isArray(navigator.userAgentData?.brands) &&
   navigator.userAgentData.brands.some(b => b.brand === 'Opera')
 if (isOpera) {
-  console.warn('Browser cache is limited in Opera. Disabling pdf-caching.')
+  debugConsole.warn('Browser cache is limited in Opera. Disabling pdf-caching.')
 }
 
 function isFlagEnabled(flag) {

+ 3 - 2
services/web/frontend/js/features/pdf-preview/util/pdf-caching-transport.js

@@ -10,6 +10,7 @@ import {
   trackPdfDownloadEnabled,
 } from './pdf-caching-flags'
 import { isNetworkError } from '../../../utils/isNetworkError'
+import { debugConsole } from '@/utils/debugging'
 
 // 30 seconds: The shutdown grace period of a clsi pre-emp instance.
 const STALE_OUTPUT_REQUEST_THRESHOLD_MS = 30 * 1000
@@ -130,7 +131,7 @@ export function generatePdfCachingTransportFactory(PDFJS) {
             throw err // This was a fallback request already. Do not retry.
           }
           err = OError.tag(err, 'optimized pdf download error', getDebugInfo())
-          console.error(err)
+          debugConsole.error(err)
           captureException(err, {
             tags: {
               fromPdfCaching: true,
@@ -160,7 +161,7 @@ export function generatePdfCachingTransportFactory(PDFJS) {
         .catch(err => {
           if (abortSignal.aborted) return
           err = OError.tag(err, 'fatal pdf download error', getDebugInfo())
-          console.error(err)
+          debugConsole.error(err)
           if (!(err instanceof PDFJS.MissingPDFException)) {
             captureException(err, {
               tags: {

+ 2 - 1
services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.js

@@ -1,5 +1,6 @@
 import { captureException } from '../../../infrastructure/error-reporter'
 import { generatePdfCachingTransportFactory } from './pdf-caching-transport'
+import { debugConsole } from '@/utils/debugging'
 
 const params = new URLSearchParams(window.location.search)
 const disableFontFace = params.get('disable-font-face') === 'true'
@@ -112,7 +113,7 @@ export default class PDFJSWrapper {
           resolve(doc)
 
           if (previousDoc) {
-            previousDoc.cleanup().catch(console.error)
+            previousDoc.cleanup().catch(debugConsole.error)
             previousDoc.destroy()
           }
         })

+ 2 - 1
services/web/frontend/js/features/project-list/components/modals/create-tag-modal.tsx

@@ -10,6 +10,7 @@ import useSelectColor from '../../hooks/use-select-color'
 import { createTag } from '../../util/api'
 import { MAX_TAG_LENGTH } from '../../util/tag'
 import { ColorPicker } from '../color-picker/color-picker'
+import { debugConsole } from '@/utils/debugging'
 
 type CreateTagModalProps = {
   id: string
@@ -39,7 +40,7 @@ export default function CreateTagModal({
     if (tagName) {
       runAsync(createTag(tagName, selectedColor))
         .then(tag => onCreate(tag))
-        .catch(console.error)
+        .catch(debugConsole.error)
     }
   }, [runAsync, tagName, selectedColor, onCreate])
 

+ 2 - 1
services/web/frontend/js/features/project-list/components/modals/delete-tag-modal.tsx

@@ -5,6 +5,7 @@ import { Tag } from '../../../../../../app/src/Features/Tags/types'
 import AccessibleModal from '../../../../shared/components/accessible-modal'
 import useAsync from '../../../../shared/hooks/use-async'
 import { deleteTag } from '../../util/api'
+import { debugConsole } from '@/utils/debugging'
 
 type DeleteTagModalProps = {
   id: string
@@ -28,7 +29,7 @@ export default function DeleteTagModal({
         .then(() => {
           onDelete(tagId)
         })
-        .catch(console.error)
+        .catch(debugConsole.error)
     },
     [runAsync, onDelete]
   )

+ 2 - 1
services/web/frontend/js/features/project-list/components/modals/edit-tag-modal.tsx

@@ -10,6 +10,7 @@ import useSelectColor from '../../hooks/use-select-color'
 import { editTag } from '../../util/api'
 import { getTagColor, MAX_TAG_LENGTH } from '../../util/tag'
 import { ColorPicker } from '../color-picker/color-picker'
+import { debugConsole } from '@/utils/debugging'
 
 type EditTagModalProps = {
   id: string
@@ -39,7 +40,7 @@ export function EditTagModal({ id, tag, onEdit, onClose }: EditTagModalProps) {
         const color = selectedColor
         runAsync(editTag(tagId, newTagName, color))
           .then(() => onEdit(tagId, newTagName, color))
-          .catch(console.error)
+          .catch(debugConsole.error)
       }
     },
     [runAsync, newTagName, selectedColor, onEdit]

+ 3 - 2
services/web/frontend/js/features/project-list/components/modals/manage-tag-modal.tsx

@@ -9,6 +9,7 @@ import { deleteTag, editTag } from '../../util/api'
 import { Tag } from '../../../../../../app/src/Features/Tags/types'
 import { getTagColor } from '../../util/tag'
 import { ColorPicker } from '../color-picker/color-picker'
+import { debugConsole } from '@/utils/debugging'
 
 type ManageTagModalProps = {
   id: string
@@ -46,7 +47,7 @@ export function ManageTagModal({
         .then(() => {
           onDelete(tagId)
         })
-        .catch(console.error)
+        .catch(debugConsole.error)
     },
     [runDeleteAsync, onDelete]
   )
@@ -56,7 +57,7 @@ export function ManageTagModal({
       if (newTagName) {
         runEditAsync(editTag(tagId, newTagName, selectedColor))
           .then(() => onEdit(tagId, newTagName, selectedColor))
-          .catch(console.error)
+          .catch(debugConsole.error)
       }
     },
     [runEditAsync, newTagName, selectedColor, onEdit]

+ 2 - 1
services/web/frontend/js/features/project-list/components/modals/rename-project-modal.tsx

@@ -15,6 +15,7 @@ import { renameProject } from '../../util/api'
 import useAsync from '../../../../shared/hooks/use-async'
 import { useProjectListContext } from '../../context/project-list-context'
 import { getUserFacingMessage } from '../../../../infrastructure/fetch-json'
+import { debugConsole } from '@/utils/debugging'
 
 type RenameProjectModalProps = {
   handleCloseModal: () => void
@@ -60,7 +61,7 @@ function RenameProjectModal({
           })
           handleCloseModal()
         })
-        .catch(console.error)
+        .catch(debugConsole.error)
     },
     [
       handleCloseModal,

+ 2 - 1
services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx

@@ -12,6 +12,7 @@ import { UserEmailData } from '../../../../../../../../types/user-email'
 import { ExposedSettings } from '../../../../../../../../types/exposed-settings'
 import { Institution } from '../../../../../../../../types/institution'
 import { useLocation } from '../../../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 type ReconfirmAffiliationProps = {
   email: UserEmailData['email']
@@ -47,7 +48,7 @@ function ReconfirmAffiliation({
         postJSON('/user/emails/send-reconfirmation', {
           body: { email },
         })
-      ).catch(console.error)
+      ).catch(debugConsole.error)
     }
   }
 

+ 2 - 1
services/web/frontend/js/features/project-list/components/notifications/groups/common.tsx

@@ -13,6 +13,7 @@ import {
 } from '../../../../../../../types/project/dashboard/notification'
 import { User } from '../../../../../../../types/user'
 import GroupInvitationNotification from './group-invitation/group-invitation'
+import { debugConsole } from '@/utils/debugging'
 
 function Common() {
   const notifications = getMeta('ol-notifications', []) as NotificationType[]
@@ -53,7 +54,7 @@ function CommonNotification({ notification }: CommonNotificationProps) {
 
     runAsync(
       postJSON(`/project/${projectId}/invite/token/${token}/accept`)
-    ).catch(console.error)
+    ).catch(debugConsole.error)
   }
 
   const { _id: id, templateKey, html } = notification

+ 2 - 1
services/web/frontend/js/features/project-list/components/notifications/groups/confirm-email.tsx

@@ -11,6 +11,7 @@ import {
 } from '../../../../../infrastructure/fetch-json'
 import { ExposedSettings } from '../../../../../../../types/exposed-settings'
 import { UserEmailData } from '../../../../../../../types/user-email'
+import { debugConsole } from '@/utils/debugging'
 
 const ssoAvailable = ({ samlProviderId, affiliation }: UserEmailData) => {
   const { hasSamlFeature, hasSamlBeta } = getMeta(
@@ -78,7 +79,7 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
       postJSON('/user/emails/resend_confirmation', {
         body: { email },
       })
-    ).catch(console.error)
+    ).catch(debugConsole.error)
   }
 
   if (isSuccess) {

+ 3 - 3
services/web/frontend/js/features/project-list/components/notifications/groups/group-invitation/hooks/use-group-invitation-notification.tsx

@@ -15,6 +15,7 @@ import {
 import { useLocation } from '../../../../../../../shared/hooks/use-location'
 import getMeta from '../../../../../../../utils/meta'
 import useAsyncDismiss from '../../../hooks/useAsyncDismiss'
+import { debugConsole } from '@/utils/debugging'
 
 const SUCCESSFUL_NOTIF_TIME_BEFORE_HIDDEN = 10 * 1000
 
@@ -85,9 +86,8 @@ export function useGroupInvitationNotification(
           setGroupInvitationStatus(GroupInvitationStatus.SuccessfullyJoined)
         })
         .catch(err => {
+          debugConsole.error(err)
           setGroupInvitationStatus(GroupInvitationStatus.Error)
-
-          console.error(err)
         })
         .finally(() => {
           // remove notification automatically in the browser
@@ -107,7 +107,7 @@ export function useGroupInvitationNotification(
           _csrf: getMeta('ol-csrfToken'),
         },
       })
-    ).catch(console.error)
+    ).catch(debugConsole.error)
   }, [runAsync])
 
   const dismissGroupInviteNotification = useCallback(() => {

+ 2 - 1
services/web/frontend/js/features/project-list/components/notifications/hooks/useAsyncDismiss.ts

@@ -1,11 +1,12 @@
 import useAsync from '../../../../../shared/hooks/use-async'
 import { deleteJSON } from '../../../../../infrastructure/fetch-json'
+import { debugConsole } from '@/utils/debugging'
 
 function useAsyncDismiss() {
   const { runAsync, ...rest } = useAsync()
 
   const handleDismiss = (id: number | string) => {
-    runAsync(deleteJSON(`/notifications/${id}`)).catch(console.error)
+    runAsync(deleteJSON(`/notifications/${id}`)).catch(debugConsole.error)
   }
 
   return { handleDismiss, ...rest }

+ 2 - 1
services/web/frontend/js/features/project-list/components/notifications/system-messages.tsx

@@ -8,6 +8,7 @@ import {
   SystemMessage as TSystemMessage,
   SuggestedLanguage,
 } from '../../../../../../types/project/dashboard/system-message'
+import { debugConsole } from '@/utils/debugging'
 
 const MESSAGE_POLL_INTERVAL = 15 * 60 * 1000
 
@@ -24,7 +25,7 @@ function SystemMessages() {
         return
       }
 
-      runAsync(getJSON('/system/messages')).catch(console.error)
+      runAsync(getJSON('/system/messages')).catch(debugConsole.error)
     }
     pollMessages()
 

+ 2 - 1
services/web/frontend/js/features/project-list/context/project-list-context.tsx

@@ -34,6 +34,7 @@ import {
   isDeletableProject,
   isLeavableProject,
 } from '../util/project'
+import { debugConsole } from '@/utils/debugging'
 
 const MAX_PROJECT_PER_PAGE = 20
 
@@ -168,7 +169,7 @@ export function ProjectListProvider({ children }: ProjectListProviderProps) {
         setLoadedProjects(data.projects)
         setTotalProjectsCount(data.totalSize)
       })
-      .catch(error => console.error(error))
+      .catch(debugConsole.error)
       .finally(() => {
         setLoadProgress(100)
       })

+ 2 - 1
services/web/frontend/js/features/settings/components/emails/add-email/input.tsx

@@ -10,6 +10,7 @@ import { Nullable } from '../../../../../../../types/utils'
 import { getJSON } from '../../../../../infrastructure/fetch-json'
 import useAbortController from '../../../../../shared/hooks/use-abort-controller'
 import domainBlocklist from '../../../domain-blocklist'
+import { debugConsole } from '@/utils/debugging'
 
 const LOCAL_AND_DOMAIN_REGEX = /([^@]+)@(.+)/
 
@@ -109,9 +110,9 @@ function Input({ onChange, handleAddNewEmail }: InputProps) {
           }
         })
         .catch(error => {
+          debugConsole.error(error)
           setSuggestion(null)
           setMatchedDomain(null)
-          console.error(error)
         })
     },
     [signal, matchedDomain]

+ 2 - 1
services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx

@@ -10,6 +10,7 @@ import { ExposedSettings } from '../../../../../../../types/exposed-settings'
 import { ssoAvailableForInstitution } from '../../../utils/sso'
 import Icon from '../../../../../shared/components/icon'
 import { useLocation } from '../../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 type ReconfirmationInfoPromptProps = {
   email: string
@@ -55,7 +56,7 @@ function ReconfirmationInfoPrompt({
             email,
           },
         })
-      ).catch(console.error)
+      ).catch(debugConsole.error)
     }
   }
 

+ 3 - 2
services/web/frontend/js/features/share-project-modal/components/link-sharing.js

@@ -13,6 +13,7 @@ import { useUserContext } from '../../../shared/context/user-context'
 import { sendMB } from '../../../infrastructure/event-tracking'
 import { getJSON } from '../../../infrastructure/fetch-json'
 import useAbortController from '../../../shared/hooks/use-abort-controller'
+import { debugConsole } from '@/utils/debugging'
 
 export default function LinkSharing({ canAddCollaborators }) {
   const [inflight, setInflight] = useState(false)
@@ -125,7 +126,7 @@ function TokenBasedSharing({ setAccessLevel, inflight, canAddCollaborators }) {
   useEffect(() => {
     getJSON(`/project/${projectId}/tokens`, { signal })
       .then(data => setTokens(data))
-      .catch(error => console.error(error))
+      .catch(debugConsole.error)
   }, [projectId, signal])
 
   return (
@@ -223,7 +224,7 @@ export function ReadOnlyTokenLink() {
   useEffect(() => {
     getJSON(`/project/${projectId}/tokens`, { signal })
       .then(data => setTokens(data))
-      .catch(error => console.error(error))
+      .catch(debugConsole.error)
   }, [projectId, signal])
 
   return (

+ 5 - 4
services/web/frontend/js/features/share-project-modal/controllers/react-share-project-modal-controller.js

@@ -4,6 +4,7 @@ import { react2angular } from 'react2angular'
 import ShareProjectModal from '../components/share-project-modal'
 import { rootContext } from '../../../shared/context/root-context'
 import { listProjectInvites, listProjectMembers } from '../utils/api'
+import { debugConsole } from '@/utils/debugging'
 
 App.component(
   'shareProjectModal',
@@ -41,8 +42,8 @@ export default App.controller(
               })
             }
           })
-          .catch(() => {
-            console.error('Error fetching members for project')
+          .catch(err => {
+            debugConsole.error('Error fetching members for project', err)
           })
       }
 
@@ -55,8 +56,8 @@ export default App.controller(
               })
             }
           })
-          .catch(() => {
-            console.error('Error fetching invites for project')
+          .catch(err => {
+            debugConsole.error('Error fetching invites for project', err)
           })
       }
     })

+ 4 - 1
services/web/frontend/js/features/source-editor/components/table-generator/contexts/editing-context.tsx

@@ -2,6 +2,7 @@ import { FC, createContext, useCallback, useContext, useState } from 'react'
 import { useCodeMirrorViewContext } from '../../codemirror-editor'
 import { useTableContext } from './table-context'
 import { TableSelection } from './selection-context'
+import { debugConsole } from '@/utils/debugging'
 
 type EditingContextData = {
   rowIndex: number
@@ -50,7 +51,9 @@ export const EditingContextProvider: FC = ({ children }) => {
       const currentText = view.state.sliceDoc(from, to)
       if (currentText !== initialContent && initialContent !== undefined) {
         // The cell has changed since we started editing, so we don't want to overwrite it
-        console.error('Cell has changed since editing started, not overwriting')
+        debugConsole.error(
+          'Cell has changed since editing started, not overwriting'
+        )
         return
       }
       setInitialContent(undefined)

+ 3 - 2
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts

@@ -13,6 +13,7 @@ import {
   extendBackwardsOverEmptyLines,
   extendForwardsOverEmptyLines,
 } from '../../../extensions/visual/selection'
+import { debugConsole } from '@/utils/debugging'
 
 /* eslint-disable no-unused-vars */
 export enum BorderTheme {
@@ -284,7 +285,7 @@ export const removeRowOrColumns = (
             const from = cellPosition.from
             const to = cellSeparators[row][cellIndex].to
             if (from === undefined || to === undefined) {
-              console.error('Failed to remove column')
+              debugConsole.error('Failed to remove column')
               return selection
             }
             changes.push({
@@ -298,7 +299,7 @@ export const removeRowOrColumns = (
             const from = cellSeparators[row][cellIndex - 1].from
             const to = cellPosition.to
             if (from === undefined || to === undefined) {
-              console.error('Failed to remove column')
+              debugConsole.error('Failed to remove column')
               return selection
             }
             changes.push({

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/annotations.ts

@@ -10,6 +10,7 @@ import {
   Text,
 } from '@codemirror/state'
 import { Annotation } from '../../../../../types/annotation'
+import { debugConsole } from '@/utils/debugging'
 
 const compileLintSourceConf = new Compartment()
 
@@ -115,7 +116,7 @@ export const setAnnotations = (doc: Text, annotations: Annotation[]) => {
         diagnostics.push(convertAnnotationToDiagnostic(doc, annotation))
       } catch (error) {
         // ignore invalid annotations
-        console.debug('invalid annotation position', error)
+        debugConsole.debug('invalid annotation position', error)
       }
     }
   }

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/cursor-highlights.ts

@@ -17,6 +17,7 @@ import {
 import { findValidPosition } from '../utils/position'
 import { Highlight } from '../../../../../types/highlight'
 import { fullHeightCoordsAtPos, getBase } from '../utils/layer'
+import { debugConsole } from '@/utils/debugging'
 
 /**
  * A custom extension that displays collaborator cursors in a separate layer.
@@ -100,7 +101,7 @@ const cursorHighlightsState = StateField.define<RangeSet<HighlightRangeValue>>({
               )
             } catch (error) {
               // ignore invalid highlights
-              console.debug('invalid highlight position', error)
+              debugConsole.debug('invalid highlight position', error)
             }
           }
         }

+ 4 - 3
services/web/frontend/js/features/source-editor/extensions/cursor-position.ts

@@ -8,6 +8,7 @@ import {
 import { EditorView, ViewPlugin } from '@codemirror/view'
 import { findValidPosition } from '../utils/position'
 import customLocalStorage from '../../../infrastructure/local-storage'
+import { debugConsole } from '@/utils/debugging'
 
 const buildStorageKey = (docId: string) => `doc.position.${docId}`
 
@@ -112,7 +113,7 @@ export const restoreCursorPosition = (
     }
   } catch (error) {
     // ignore invalid cursor position
-    console.debug('invalid cursor position', error)
+    debugConsole.debug('invalid cursor position', error)
     return {}
   }
 }
@@ -142,7 +143,7 @@ export const setCursorLineAndScroll = (
     selectionRange = EditorSelection.cursor(pos)
   } catch (error) {
     // ignore invalid cursor position
-    console.debug('invalid cursor position', error)
+    debugConsole.debug('invalid cursor position', error)
   }
 
   if (selectionRange) {
@@ -157,7 +158,7 @@ export const setCursorPositionAndScroll = (view: EditorView, pos: number) => {
     selectionRange = EditorSelection.cursor(pos)
   } catch (error) {
     // ignore invalid cursor position
-    console.debug('invalid cursor position', error)
+    debugConsole.debug('invalid cursor position', error)
   }
 
   if (selectionRange) {

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/figure-modal.ts

@@ -8,6 +8,7 @@ import { EditorView } from '@codemirror/view'
 import { addEffectListener, removeEffectListener } from './effect-listeners'
 import { setMetadataEffect } from './language'
 import getMeta from '../../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 type NestedReadonly<T> = {
   readonly [P in keyof T]: NestedReadonly<T[P]>
@@ -136,7 +137,7 @@ export function waitForFileTreeUpdate(view: EditorView) {
   const abortController = new AbortController()
   const promise = new Promise<void>(resolve => {
     const abort = () => {
-      console.warn('Aborting wait for file tree update')
+      debugConsole.warn('Aborting wait for file tree update')
       removeEffectListener(view, setMetadataEffect, listener)
       resolve()
     }

+ 5 - 4
services/web/frontend/js/features/source-editor/extensions/realtime.ts

@@ -3,6 +3,7 @@ import { EditorView, ViewPlugin } from '@codemirror/view'
 import { EventEmitter } from 'events'
 import { CurrentDoc } from '../../../../../types/current-doc'
 import { ShareDoc } from '../../../../../types/share-doc'
+import { debugConsole } from '@/utils/debugging'
 
 /*
  * Integrate CodeMirror 6 with the real-time system, via ShareJS.
@@ -51,7 +52,7 @@ export const realtime = (
   const ensureRealtimePlugin = EditorView.updateListener.of(update => {
     if (!update.view.plugin(realtimePlugin)) {
       const message = 'The realtime extension has been destroyed!!'
-      console.log(message)
+      debugConsole.warn(message)
       if (currentDoc.doc) {
         // display the "out of sync" modal
         currentDoc.doc.emit('error', message)
@@ -119,9 +120,9 @@ export class EditorFacade extends EventEmitter {
 
         if (editorText !== otText) {
           shareDoc.emit('error', 'Text does not match in CodeMirror 6')
-          console.error('Text does not match!')
-          console.error('editor: ' + editorText)
-          return console.error('ot:     ' + otText)
+          debugConsole.error('Text does not match!')
+          debugConsole.error('editor: ' + editorText)
+          debugConsole.error('ot:     ' + otText)
         }
       }, 0)
     }

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/scroll-position.ts

@@ -8,6 +8,7 @@ import {
   TransactionSpec,
 } from '@codemirror/state'
 import { toggleVisualEffect } from './visual/visual'
+import { debugConsole } from '@/utils/debugging'
 
 const buildStorageKey = (docId: string) => `doc.position.${docId}`
 
@@ -157,7 +158,7 @@ const scrollStoredLineToTop = (doc: Text, docId: string): TransactionSpec => {
     }
   } catch (e) {
     // ignore invalid line number
-    console.error(e)
+    debugConsole.error(e)
     return {}
   }
 }

+ 4 - 3
services/web/frontend/js/features/source-editor/extensions/spelling/context-menu.ts

@@ -8,6 +8,7 @@ import { EditorView, showTooltip, Tooltip, keymap } from '@codemirror/view'
 import { addIgnoredWord } from './ignored-words'
 import { learnWordRequest } from './backend'
 import { Word, Mark, getMarkAtPosition } from './spellchecker'
+import { debugConsole } from '@/utils/debugging'
 
 const ITEMS_TO_SHOW = 8
 
@@ -54,7 +55,7 @@ const handleContextMenuEvent = (event: MouseEvent, view: EditorView) => {
 
   const targetWord = value.spec.word
   if (!targetWord) {
-    console.debug(
+    debugConsole.debug(
       '>> spelling no word associated with decorated range, stopping'
     )
     return
@@ -286,7 +287,7 @@ const handleLearnWord = async function (word: Word, view: EditorView) {
       effects: [addIgnoredWord.of(word), hideSpellingMenu.of(null)],
     })
   } catch (err) {
-    console.error(err)
+    debugConsole.error(err)
   }
 }
 
@@ -303,7 +304,7 @@ const handleCorrectWord = (word: Word, text: string, view: EditorView) => {
   // Defend against erroneous replacement, if the word at this
   // position is not actually what we think it is
   if (existingText !== word.text) {
-    console.debug(
+    debugConsole.debug(
       '>> spelling word-to-correct does not match, stopping',
       tooltip.pos,
       tooltip.end,

+ 2 - 5
services/web/frontend/js/features/source-editor/extensions/spelling/spellchecker.ts

@@ -13,13 +13,10 @@ import {
   NormalTextSpan,
 } from '../../utils/tree-query'
 import { waitForParser } from '../wait-for-parser'
-
-const DEBUG = window ? window.sl_debugging : false
+import { debugConsole } from '@/utils/debugging'
 
 const _log = (...args: any) => {
-  if (DEBUG) {
-    console.debug('[SpellChecker]: ', ...args)
-  }
+  debugConsole.debug('[SpellChecker]: ', ...args)
 }
 
 /*

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/track-changes.ts

@@ -30,6 +30,7 @@ import {
   Operation,
 } from '../../../../../types/change'
 import { ChangeManager } from './changes/change-manager'
+import { debugConsole } from '@/utils/debugging'
 
 const clearChangesEffect = StateEffect.define()
 const buildChangesEffect = StateEffect.define()
@@ -192,7 +193,7 @@ const buildChangeDecorations = (currentDoc: CurrentDoc) => {
       decorations.push(...createChangeRange(change, currentDoc))
     } catch (error) {
       // ignore invalid changes
-      console.debug('invalid change position', error)
+      debugConsole.debug('invalid change position', error)
     }
   }
 

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/visual/paste-html.ts

@@ -5,6 +5,7 @@ import {
   pastedContent,
   storePastedContent,
 } from './pasted-content'
+import { debugConsole } from '@/utils/debugging'
 
 export const pasteHtml = [
   Prec.highest(
@@ -65,7 +66,7 @@ export const pasteHtml = [
 
           return true
         } catch (error) {
-          console.error(error)
+          debugConsole.error(error)
 
           // fall back to the default paste handler
           return false

+ 2 - 3
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/graphics.ts

@@ -2,6 +2,7 @@ import { EditorView, WidgetType } from '@codemirror/view'
 import { placeSelectionInsideBlock } from '../selection'
 import { isEqual } from 'lodash'
 import { FigureData } from '../../figure-modal'
+import { debugConsole } from '@/utils/debugging'
 
 export class GraphicsWidget extends WidgetType {
   destroyed = false
@@ -99,9 +100,7 @@ export class GraphicsWidget extends WidgetType {
         {
           const canvas = document.createElement('canvas')
           canvas.classList.add('ol-cm-graphics')
-          this.renderPDF(view, canvas, preview.url).catch(error => {
-            console.error(error)
-          })
+          this.renderPDF(view, canvas, preview.url).catch(debugConsole.error)
           element.append(canvas)
         }
         break

+ 2 - 1
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/tabular.tsx

@@ -7,6 +7,7 @@ import {
   ParsedTableData,
   generateTable,
 } from '../../../components/table-generator/utils'
+import { debugConsole } from '@/utils/debugging'
 
 export class TabularWidget extends WidgetType {
   private element: HTMLElement | undefined
@@ -23,7 +24,7 @@ export class TabularWidget extends WidgetType {
     try {
       this.parseResult = generateTable(tabularNode, state)
     } catch (e) {
-      console.error(e)
+      debugConsole.error(e)
       this.parseResult = null
     }
   }

+ 2 - 3
services/web/frontend/js/features/source-editor/extensions/visual/visual.ts

@@ -26,6 +26,7 @@ import { selectDecoratedArgument } from './select-decorated-argument'
 import { pasteHtml } from './paste-html'
 import { commandTooltip } from '../command-tooltip'
 import { tableGeneratorTheme } from './table-generator'
+import { debugConsole } from '@/utils/debugging'
 
 type Options = {
   visual: boolean
@@ -163,9 +164,7 @@ const showContentWhenParsed = [
               window.clearTimeout(fallbackTimer)
               // show the content, in a timeout so the decorations can build first
               window.setTimeout(showContent)
-            }).catch(error => {
-              console.error(error)
-            })
+            }).catch(debugConsole.error)
           })
         }
       },

+ 2 - 1
services/web/frontend/js/features/source-editor/languages/latex/debug-print.ts

@@ -25,6 +25,7 @@
 
 import { Text } from '@codemirror/state'
 import { Input, NodeType, SyntaxNode, Tree, TreeCursor } from '@lezer/common'
+import { debugConsole } from '@/utils/debugging'
 
 class StringInput implements Input {
   private input: string
@@ -278,5 +279,5 @@ export function logTree(
   input: string,
   options?: PrintTreeOptions
 ): void {
-  console.log(printTree(tree, input, options))
+  debugConsole.warn(printTree(tree, input, options))
 }

+ 2 - 1
services/web/frontend/js/features/source-editor/languages/latex/latex-indent-service.ts

@@ -1,4 +1,5 @@
 import { indentService } from '@codemirror/language'
+import { debugConsole } from '@/utils/debugging'
 
 export const latexIndentService = () => {
   return indentService.of((indentContext, pos) => {
@@ -10,7 +11,7 @@ export const latexIndentService = () => {
         return whitespace[0].length
       }
     } catch (err) {
-      console.error('Error in CM indentService', err)
+      debugConsole.error('Error in CM indentService', err)
     }
     return null
   })

+ 2 - 1
services/web/frontend/js/features/source-editor/lezer-latex/print-tree.mjs

@@ -23,6 +23,7 @@
 // SOFTWARE.
 import { Text } from '@codemirror/state'
 import { Tree, TreeCursor } from '@lezer/common'
+import { debugConsole } from '@/utils/debugging'
 
 class StringInput {
   constructor(input) {
@@ -211,5 +212,5 @@ function locAt(text, pos) {
 }
 
 export function logTree(tree, input, options) {
-  console.log(printTree(tree, input, options))
+  debugConsole.warn(printTree(tree, input, options))
 }

+ 2 - 2
services/web/frontend/js/features/source-editor/themes/convert.js → services/web/frontend/js/features/source-editor/themes/scripts/convert.js

@@ -20,9 +20,9 @@ const overrides = require('./overrides.json')
 const { merge } = require('lodash')
 
 // CSS files from https://github.com/overleaf/ace/tree/overleaf/lib/ace/theme copied into the "ace" folder
-const themePaths = globby.sync(['ace/*.css'], { cwd: __dirname })
+const themePaths = globby.sync(['ace/*.css'], { cwd: path.dirname(__dirname) })
 
-const outputDir = path.join(__dirname, 'cm6')
+const outputDir = path.join(path.dirname(__dirname), 'cm6')
 
 // from js/ide.js
 const darkThemes = [

+ 0 - 0
services/web/frontend/js/features/source-editor/themes/overrides.json → services/web/frontend/js/features/source-editor/themes/scripts/overrides.json


+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/leave-group-modal.tsx

@@ -5,6 +5,7 @@ import { deleteJSON } from '../../../../infrastructure/fetch-json'
 import AccessibleModal from '../../../../shared/components/accessible-modal'
 import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
 import { useLocation } from '../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 export const LEAVE_GROUP_MODAL_ID = 'leave-group'
 
@@ -26,7 +27,7 @@ export default function LeaveGroupModal() {
       await deleteJSON(`/subscription/group/user?${params}`)
       location.reload()
     } catch (error) {
-      console.log('something went wrong', error)
+      debugConsole.error('something went wrong', error)
       setInflight(false)
     }
   }, [location, leavingGroupId])

+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/managed-institution.tsx

@@ -4,6 +4,7 @@ import { postJSON } from '../../../../infrastructure/fetch-json'
 import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
 import { Institution } from './managed-institutions'
 import { RowLink } from './row-link'
+import { debugConsole } from '@/utils/debugging'
 
 type ManagedInstitutionProps = {
   institution: Institution
@@ -27,7 +28,7 @@ export default function ManagedInstitution({
           institution.metricsEmail.optedOutUserIds = data
           updateManagedInstitution(institution)
         } catch (error) {
-          console.error(error)
+          debugConsole.error(error)
         }
         setSubscriptionChanging(false)
       }

+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/reactivate-subscription.tsx

@@ -4,6 +4,7 @@ import { reactivateSubscriptionUrl } from '../../data/subscription-url'
 import useAsync from '../../../../shared/hooks/use-async'
 import { useLocation } from '../../../../shared/hooks/use-location'
 import getMeta from '../../../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 function ReactivateSubscription() {
   const { t } = useTranslation()
@@ -11,7 +12,7 @@ function ReactivateSubscription() {
   const location = useLocation()
 
   const handleReactivate = () => {
-    runAsync(postJSON(reactivateSubscriptionUrl)).catch(console.error)
+    runAsync(postJSON(reactivateSubscriptionUrl)).catch(debugConsole.error)
   }
 
   if (isSuccess) {

+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/cancel-subscription.tsx

@@ -15,6 +15,7 @@ import GenericErrorAlert from '../../../generic-error-alert'
 import DowngradePlanButton from './downgrade-plan-button'
 import ExtendTrialButton from './extend-trial-button'
 import { useLocation } from '../../../../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 const planCodeToDowngradeTo = 'paid-personal'
 
@@ -180,7 +181,7 @@ export function CancelSubscription() {
       await runAsyncCancel(postJSON(cancelSubscriptionUrl))
       location.assign(redirectAfterCancelSubscriptionUrl)
     } catch (e) {
-      console.error(e)
+      debugConsole.error(e)
     }
   }
 

+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/downgrade-plan-button.tsx

@@ -4,6 +4,7 @@ import { postJSON } from '../../../../../../../infrastructure/fetch-json'
 import { subscriptionUpdateUrl } from '../../../../../data/subscription-url'
 import ActionButtonText from '../../../action-button-text'
 import { useLocation } from '../../../../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 export default function DowngradePlanButton({
   isButtonDisabled,
@@ -31,7 +32,7 @@ export default function DowngradePlanButton({
       )
       location.reload()
     } catch (e) {
-      console.error(e)
+      debugConsole.error(e)
     }
   }
 

+ 2 - 1
services/web/frontend/js/features/subscription/components/dashboard/states/active/cancel-plan/extend-trial-button.tsx

@@ -3,6 +3,7 @@ import { putJSON } from '../../../../../../../infrastructure/fetch-json'
 import { extendTrialUrl } from '../../../../../data/subscription-url'
 import ActionButtonText from '../../../action-button-text'
 import { useLocation } from '../../../../../../../shared/hooks/use-location'
+import { debugConsole } from '@/utils/debugging'
 
 export default function ExtendTrialButton({
   isButtonDisabled,
@@ -24,7 +25,7 @@ export default function ExtendTrialButton({
       await runAsyncSecondaryAction(putJSON(extendTrialUrl))
       location.reload()
     } catch (e) {
-      console.error(e)
+      debugConsole.error(e)
     }
   }
 

+ 4 - 3
services/web/frontend/js/features/subscription/context/subscription-dashboard-context.tsx

@@ -27,6 +27,7 @@ import {
 } from '../util/recurly-pricing'
 import { isRecurlyLoaded } from '../util/is-recurly-loaded'
 import { SubscriptionDashModalIds } from '../../../../../types/subscription/dashboard/modal-ids'
+import { debugConsole } from '@/utils/debugging'
 
 type SubscriptionDashboardContextValue = {
   groupPlanToChangeToCode: string
@@ -166,13 +167,13 @@ export function SubscriptionDashboardProvider({
               plan.displayPrice = priceData.totalForDisplay
             }
           } catch (error) {
-            console.error(error)
+            debugConsole.error(error)
           }
         }
         setPlans(plansWithoutDisplayPrice)
         setQueryingIndividualPlansData(false)
       }
-      fetchPlansDisplayPrices().catch(console.error)
+      fetchPlansDisplayPrices().catch(debugConsole.error)
     }
   }, [personalSubscription, plansWithoutDisplayPrice])
 
@@ -199,7 +200,7 @@ export function SubscriptionDashboardProvider({
             groupPlanToChangeToUsage
           )
         } catch (e) {
-          console.error(e)
+          debugConsole.error(e)
           setGroupPlanToChangeToPriceError(true)
         }
         setQueryingGroupPlanToChangeToPrice(false)

+ 2 - 3
services/web/frontend/js/features/subscription/util/recurly-pricing.ts

@@ -2,15 +2,14 @@ import { SubscriptionPricingState } from '@recurly/recurly-js'
 import { PriceForDisplayData } from '../../../../../types/subscription/plan'
 import { currencies, CurrencyCode } from '../data/currency'
 import { getRecurlyGroupPlanCode } from './recurly-group-plan-code'
+import { debugConsole } from '@/utils/debugging'
 
 function queryRecurlyPlanPrice(planCode: string, currency: CurrencyCode) {
   return new Promise(resolve => {
     recurly.Pricing.Subscription()
       .plan(planCode, { quantity: 1 })
       .currency(currency)
-      .catch(function (error) {
-        console.error(error)
-      })
+      .catch(debugConsole.error)
       .done(response => {
         if (response) {
           resolve(response)

+ 3 - 2
services/web/frontend/js/ide.js

@@ -71,6 +71,7 @@ import './features/history/controllers/history-file-tree-controller'
 import { cleanupServiceWorker } from './utils/service-worker-cleanup'
 import { reportCM6Perf } from './infrastructure/cm6-performance'
 import { reportAcePerf } from './ide/editor/ace-performance'
+import { debugConsole } from '@/utils/debugging'
 
 App.controller(
   'IdeController',
@@ -427,7 +428,7 @@ If the project has been renamed please look in your project list for a new proje
         !/.*Chromium\/.*/.test(userAgent)
     } catch (error) {
       err = error
-      console.error(err)
+      debugConsole.error(err)
     }
 
     if (ide.browserIsSafari) {
@@ -448,7 +449,7 @@ If the project has been renamed please look in your project list for a new proje
       }
     } catch (error1) {
       err = error1
-      console.error(err)
+      debugConsole.error(err)
     }
 
     // User can append ?ft=somefeature to url to activate a feature toggle

+ 32 - 31
services/web/frontend/js/ide/connection/ConnectionManager.js

@@ -12,6 +12,7 @@
 
 import SocketIoShim from './SocketIoShim'
 import getMeta from '../../utils/meta'
+import { debugConsole, debugging } from '@/utils/debugging'
 
 let ConnectionManager
 const ONEHOUR = 1000 * 60 * 60
@@ -42,7 +43,7 @@ export default ConnectionManager = (function () {
           // try reloading and falling back to the siteUrl
           window.location = window.location.href + '?ws=fallback'
         }
-        console.error(
+        debugConsole.error(
           'Socket.io javascript not loaded. Please check that the real-time service is running and accessible.'
         )
         this.ide.socket = SocketIoShim.stub()
@@ -59,7 +60,7 @@ export default ConnectionManager = (function () {
 
       // trigger a reconnect immediately if network comes back online
       window.addEventListener('online', () => {
-        sl_console.log('[online] browser notified online')
+        debugConsole.log('[online] browser notified online')
         if (!this.connected) {
           return this.tryReconnectWithRateLimit({ force: true })
         }
@@ -78,7 +79,7 @@ export default ConnectionManager = (function () {
       this.joinProjectRetryInterval = this.JOIN_PROJECT_RETRY_INTERVAL
 
       this.$scope.connection = {
-        debug: sl_debugging,
+        debug: debugging,
         reconnecting: false,
         stillReconnecting: false,
         // If we need to force everyone to reload the editor
@@ -157,7 +158,7 @@ export default ConnectionManager = (function () {
           )
         }
         this.updateConnectionManagerState('error')
-        sl_console.log('socket.io error', err)
+        debugConsole.log('socket.io error', err)
         if (this.wsUrl && !window.location.href.match(/ws=fallback/)) {
           // if we tried to load a custom websocket location and failed
           // try reloading and falling back to the siteUrl
@@ -180,7 +181,7 @@ export default ConnectionManager = (function () {
         // state should be 'connecting'...
         // remove connection error handler when connected, avoid unwanted fallbacks
         this.ide.socket.removeListener('error', connectionErrorHandler)
-        sl_console.log('[socket.io connect] Connected')
+        debugConsole.log('[socket.io connect] Connected')
         this.updateConnectionManagerState('authenticating')
       })
 
@@ -192,7 +193,7 @@ export default ConnectionManager = (function () {
       this.ide.socket.on('connectionAccepted', (_, publicId) => {
         this.ide.socket.publicId = publicId || this.ide.socket.socket.sessionid
         // state should be 'authenticating'...
-        sl_console.log('[socket.io connectionAccepted] allowed to connect')
+        debugConsole.log('[socket.io connectionAccepted] allowed to connect')
         this.connected = true
         this.gracefullyReconnecting = false
         this.ide.pushEvent('connected')
@@ -215,7 +216,7 @@ export default ConnectionManager = (function () {
         'joinProjectResponse',
         ({ publicId, project, permissionsLevel, protocolVersion }) => {
           this.ide.socket.publicId = publicId
-          sl_console.log('[socket.io bootstrap] ready for joinDoc')
+          debugConsole.log('[socket.io bootstrap] ready for joinDoc')
           this.connected = true
           this.gracefullyReconnecting = false
           this.ide.pushEvent('connected')
@@ -240,7 +241,7 @@ export default ConnectionManager = (function () {
 
       this.ide.socket.on('connectionRejected', err => {
         // state should be 'authenticating'...
-        sl_console.log(
+        debugConsole.log(
           '[socket.io connectionRejected] session not valid or other connection error'
         )
         // real-time sends a 'retry' message if the process was shutting down
@@ -279,7 +280,7 @@ export default ConnectionManager = (function () {
       // "connect" event.
 
       this.ide.socket.on('disconnect', () => {
-        sl_console.log('[socket.io disconnect] Disconnected')
+        debugConsole.log('[socket.io disconnect] Disconnected')
         this.connected = false
         this.ide.pushEvent('disconnected')
 
@@ -320,7 +321,7 @@ The editor will refresh automatically in ${delay} seconds.\
       })
 
       this.ide.socket.on('reconnectGracefully', () => {
-        sl_console.log('Reconnect gracefully')
+        debugConsole.log('Reconnect gracefully')
         this.reconnectGracefully()
       })
     }
@@ -329,7 +330,7 @@ The editor will refresh automatically in ${delay} seconds.\
       this.$scope.$apply(() => {
         this.$scope.connection.jobId += 1
         const jobId = this.$scope.connection.jobId
-        sl_console.log(
+        debugConsole.log(
           `[updateConnectionManagerState ${jobId}] from ${this.$scope.connection.state} to ${state}`
         )
         this.$scope.connection.state = state
@@ -376,7 +377,7 @@ The editor will refresh automatically in ${delay} seconds.\
         } else if (state === 'error') {
           // something is wrong
         } else {
-          sl_console.log(
+          debugConsole.log(
             `[WARN] [updateConnectionManagerState ${jobId}] got unrecognised state ${state}`
           )
         }
@@ -391,7 +392,7 @@ The editor will refresh automatically in ${delay} seconds.\
         return true
       }
 
-      sl_console.log(
+      debugConsole.log(
         `[WARN] [state mismatch] expected state ${state}${
           jobId ? '/' + jobId : ''
         } when in ${this.$scope.connection.state}/${
@@ -404,7 +405,7 @@ The editor will refresh automatically in ${delay} seconds.\
     // Error reporting, which can reload the page if appropriate
 
     reportConnectionError(err) {
-      sl_console.log('[socket.io] reporting connection error')
+      debugConsole.log('[socket.io] reporting connection error')
       this.updateConnectionManagerState('error')
       if (
         (err != null ? err.message : undefined) === 'not authorized' ||
@@ -425,12 +426,12 @@ Something went wrong connecting to your project. Please refresh if this continue
     }
 
     joinProject(connectionId) {
-      sl_console.log(`[joinProject ${connectionId}] joining...`)
+      debugConsole.log(`[joinProject ${connectionId}] joining...`)
       // Note: if the "joinProject" message doesn't reach the server
       // (e.g. if we are in a disconnected state at this point) the
       // callback will never be executed
       if (!this.expectConnectionManagerState('joining', connectionId)) {
-        sl_console.log(
+        debugConsole.log(
           `[joinProject ${connectionId}] aborting with stale connection`
         )
         return
@@ -477,7 +478,7 @@ Something went wrong connecting to your project. Please refresh if this continue
           return
         }
         if (err.code === 'TooManyRequests') {
-          sl_console.log(
+          debugConsole.log(
             `[joinProject ${connectionId}] retrying: ${err.message}`
           )
           setTimeout(
@@ -525,16 +526,16 @@ Something went wrong connecting to your project. Please refresh if this continue
 
     disconnect(options) {
       if (options && options.permanent) {
-        sl_console.log('[disconnect] shutting down ConnectionManager')
+        debugConsole.log('[disconnect] shutting down ConnectionManager')
         this.updateConnectionManagerState('inactive')
         this.shuttingDown = true // prevent reconnection attempts
       } else if (this.ide.socket.socket && !this.ide.socket.socket.connected) {
-        sl_console.log(
+        debugConsole.log(
           '[socket.io] skipping disconnect because socket.io has not connected'
         )
         return
       }
-      sl_console.log('[socket.io] disconnecting client')
+      debugConsole.log('[socket.io] disconnecting client')
       return this.ide.socket.disconnect()
     }
 
@@ -542,7 +543,7 @@ Something went wrong connecting to your project. Please refresh if this continue
       this.updateConnectionManagerState('waitingCountdown')
       const connectionId = this.$scope.connection.jobId
       let countdown
-      sl_console.log('[ConnectionManager] starting autoreconnect countdown')
+      debugConsole.log('[ConnectionManager] starting autoreconnect countdown')
       const twoMinutes = 2 * 60 * 1000
       if (
         this.lastUserAction != null &&
@@ -579,7 +580,7 @@ Something went wrong connecting to your project. Please refresh if this continue
     stopReconnectCountdownTimer() {
       // clear timeout and set to null so we know there is no countdown running
       if (this.countdownTimeoutId != null) {
-        sl_console.log(
+        debugConsole.log(
           '[ConnectionManager] cancelling existing reconnect timer'
         )
         clearTimeout(this.countdownTimeoutId)
@@ -595,13 +596,13 @@ Something went wrong connecting to your project. Please refresh if this continue
       if (
         !this.expectConnectionManagerState('waitingCountdown', connectionId)
       ) {
-        sl_console.log(
+        debugConsole.log(
           `[ConnectionManager] Aborting stale countdown ${connectionId}`
         )
         return
       }
 
-      sl_console.log(
+      debugConsole.log(
         '[ConnectionManager] decreasing countdown',
         this.$scope.connection.reconnection_countdown
       )
@@ -623,7 +624,7 @@ Something went wrong connecting to your project. Please refresh if this continue
     }
 
     tryReconnect() {
-      sl_console.log('[ConnectionManager] tryReconnect')
+      debugConsole.log('[ConnectionManager] tryReconnect')
       if (
         this.connected ||
         this.shuttingDown ||
@@ -632,20 +633,20 @@ Something went wrong connecting to your project. Please refresh if this continue
         return
       }
       this.updateConnectionManagerState('reconnecting')
-      sl_console.log('[ConnectionManager] Starting new connection')
+      debugConsole.log('[ConnectionManager] Starting new connection')
 
       const removeHandler = () => {
         this.ide.socket.removeListener('error', handleFailure)
         this.ide.socket.removeListener('connect', handleSuccess)
       }
       const handleFailure = () => {
-        sl_console.log('[ConnectionManager] tryReconnect: failed')
+        debugConsole.log('[ConnectionManager] tryReconnect: failed')
         removeHandler()
         this.updateConnectionManagerState('reconnectFailed')
         this.tryReconnectWithRateLimit({ force: true })
       }
       const handleSuccess = () => {
-        sl_console.log('[ConnectionManager] tryReconnect: success')
+        debugConsole.log('[ConnectionManager] tryReconnect: success')
         removeHandler()
       }
       this.ide.socket.on('error', handleFailure)
@@ -703,7 +704,7 @@ Something went wrong connecting to your project. Please refresh if this continue
         this.reconnectGracefullyStarted = new Date()
       } else {
         if (!force) {
-          sl_console.log(
+          debugConsole.log(
             '[reconnectGracefully] reconnection is already in process, so skipping'
           )
           return
@@ -716,12 +717,12 @@ Something went wrong connecting to your project. Please refresh if this continue
         new Date() - this.reconnectGracefullyStarted >
         this.MAX_RECONNECT_GRACEFULLY_INTERVAL
       if (userIsInactive || maxIntervalReached) {
-        sl_console.log(
+        debugConsole.log(
           "[reconnectGracefully] User didn't do anything for last 5 seconds, reconnecting"
         )
         this._reconnectGracefullyNow()
       } else {
-        sl_console.log(
+        debugConsole.log(
           '[reconnectGracefully] User is working, will try again in 5 seconds'
         )
         this.updateConnectionManagerState('waitingGracefully')

+ 3 - 1
services/web/frontend/js/ide/connection/EditorWatchdogManager.js

@@ -75,6 +75,8 @@
   See also: https://developer.mozilla.org/en-US/docs/Web/API/Performance/now
  */
 
+import { debugConsole } from '@/utils/debugging'
+
 // TIMEOUT specifies the timeout for edits into a single ShareJsDoc.
 const TIMEOUT = 60 * 1000
 // GLOBAL_TIMEOUT specifies the timeout for edits into any ShareJSDoc.
@@ -229,6 +231,6 @@ export default class EditorWatchdogManager {
   }
 
   _log() {
-    sl_console.log(`[EditorWatchdogManager] ${this.scope}:`, ...arguments)
+    debugConsole.log(`[EditorWatchdogManager] ${this.scope}:`, ...arguments)
   }
 }

+ 5 - 3
services/web/frontend/js/ide/connection/SocketIoShim.js

@@ -1,5 +1,7 @@
 /* global io */
 
+import { debugConsole } from '@/utils/debugging'
+
 class SocketShimBase {
   static connect(url, options) {
     return new SocketShimBase()
@@ -205,14 +207,14 @@ class SocketShimV2 extends SocketShimBase {
 
 let current
 if (typeof io === 'undefined' || !io) {
-  sl_console.log('[socket.io] Shim: socket.io is not loaded, returning noop')
+  debugConsole.log('[socket.io] Shim: socket.io is not loaded, returning noop')
   current = SocketShimNoop
 } else if (typeof io.version === 'string' && io.version.slice(0, 1) === '0') {
-  sl_console.log('[socket.io] Shim: detected v0')
+  debugConsole.log('[socket.io] Shim: detected v0')
   current = SocketShimV0
 } else {
   // socket.io v2 does not have a global io.version attribute.
-  sl_console.log('[socket.io] Shim: detected v2')
+  debugConsole.log('[socket.io] Shim: detected v2')
   current = SocketShimV2
 }
 

+ 24 - 23
services/web/frontend/js/ide/editor/Document.js

@@ -19,6 +19,7 @@
 import RangesTracker from '@overleaf/ranges-tracker'
 import EventEmitter from '../../utils/EventEmitter'
 import ShareJsDoc from './ShareJsDoc'
+import { debugConsole } from '@/utils/debugging'
 let Document
 
 export default Document = (function () {
@@ -37,18 +38,18 @@ export default Document = (function () {
       // joinDoc:existing code path on an existing doc that doesn't have any
       // local changes and getting an error if its version is too old.
       if (this.openDocs[doc_id]) {
-        sl_console.log(
+        debugConsole.log(
           `[getDocument] Cleaning up existing document instance for ${doc_id}`
         )
         this.openDocs[doc_id]._cleanUp()
       }
       if (this.openDocs[doc_id] == null) {
-        sl_console.log(
+        debugConsole.log(
           `[getDocument] Creating new document instance for ${doc_id}`
         )
         this.openDocs[doc_id] = new Document(ide, doc_id)
       } else {
-        sl_console.log(
+        debugConsole.log(
           `[getDocument] Returning existing document instance for ${doc_id}`
         )
       }
@@ -286,7 +287,7 @@ export default Document = (function () {
       this.wantToBeJoined = false
       this._cancelJoin()
       if (this.doc != null && this.doc.hasBufferedOps()) {
-        sl_console.log(
+        debugConsole.log(
           '[leave] Doc has buffered ops, pushing callback for later'
         )
         if (!this._leaveCallbacks) {
@@ -294,10 +295,10 @@ export default Document = (function () {
         }
         return this._leaveCallbacks.push(callback)
       } else if (!this.connected) {
-        sl_console.log('[leave] Not connected, returning now')
+        debugConsole.log('[leave] Not connected, returning now')
         return callback()
       } else {
-        sl_console.log('[leave] Leaving now')
+        debugConsole.log('[leave] Leaving now')
         return this._leaveDoc(callback)
       }
     }
@@ -366,12 +367,12 @@ export default Document = (function () {
       if (inflightOp == null && pendingOp == null) {
         // there's nothing going on, this is ok.
         saved = true
-        sl_console.log('[pollSavedStatus] no inflight or pending ops')
+        debugConsole.log('[pollSavedStatus] no inflight or pending ops')
       } else if (inflightOp != null && inflightOp === this.oldInflightOp) {
         // The same inflight op has been sitting unacked since we
         // last checked, this is bad.
         saved = false
-        sl_console.log('[pollSavedStatus] inflight op is same as before')
+        debugConsole.log('[pollSavedStatus] inflight op is same as before')
       } else if (
         pendingOp != null &&
         recentAck &&
@@ -380,7 +381,7 @@ export default Document = (function () {
         // There is an op waiting to go to server but it is small and
         // within the flushDelay, this is ok for now.
         saved = true
-        sl_console.log(
+        debugConsole.log(
           '[pollSavedStatus] pending op (small with recent ack) assume ok',
           pendingOp,
           pendingOpSize
@@ -388,7 +389,7 @@ export default Document = (function () {
       } else {
         // In any other situation, assume the document is unsaved.
         saved = false
-        sl_console.log(
+        debugConsole.log(
           `[pollSavedStatus] assuming not saved (inflightOp?: ${
             inflightOp != null
           }, pendingOp?: ${pendingOp != null})`
@@ -424,7 +425,7 @@ export default Document = (function () {
         window.disconnectOnAck != null &&
         Math.random() < window.disconnectOnAck
       ) {
-        sl_console.log('Disconnecting on ack', update)
+        debugConsole.log('Disconnecting on ack', update)
         window._ide.socket.socket.disconnect()
         // Pretend we never received the ack
         return
@@ -433,7 +434,7 @@ export default Document = (function () {
       if (window.dropAcks != null && Math.random() < window.dropAcks) {
         if (update.op == null) {
           // Only drop our own acks, not collaborator updates
-          sl_console.log('Simulating a lost ack', update)
+          debugConsole.log('Simulating a lost ack', update)
           return
         }
       }
@@ -455,7 +456,7 @@ export default Document = (function () {
     }
 
     _onDisconnect() {
-      sl_console.log('[onDisconnect] disconnecting')
+      debugConsole.log('[onDisconnect] disconnecting')
       this.connected = false
       this.joined = false
       return this.doc != null
@@ -464,7 +465,7 @@ export default Document = (function () {
     }
 
     _onReconnect() {
-      sl_console.log('[onReconnect] reconnected (joined project)')
+      debugConsole.log('[onReconnect] reconnected (joined project)')
       this.ide.pushEvent('reconnected:afterJoinProject')
 
       this.connected = true
@@ -472,7 +473,7 @@ export default Document = (function () {
         this.wantToBeJoined ||
         (this.doc != null ? this.doc.hasBufferedOps() : undefined)
       ) {
-        sl_console.log(
+        debugConsole.log(
           `[onReconnect] Rejoining (wantToBeJoined: ${
             this.wantToBeJoined
           } OR hasBufferedOps: ${
@@ -584,7 +585,7 @@ export default Document = (function () {
           return result
         })()
       } catch (err) {
-        return console.log(err)
+        debugConsole.error(err)
       }
     }
 
@@ -595,14 +596,14 @@ export default Document = (function () {
       this.ide.pushEvent('leaveDoc', {
         doc_id: this.doc_id,
       })
-      sl_console.log('[_leaveDoc] Sending leaveDoc request')
+      debugConsole.log('[_leaveDoc] Sending leaveDoc request')
       return this.ide.socket.emit('leaveDoc', this.doc_id, error => {
         if (error != null) {
           return callback(error)
         }
         this.joined = false
         for (callback of Array.from(this._leaveCallbacks || [])) {
-          sl_console.log('[_leaveDoc] Calling buffered callback', callback)
+          debugConsole.log('[_leaveDoc] Calling buffered callback', callback)
           callback(error)
         }
         delete this._leaveCallbacks
@@ -613,12 +614,12 @@ export default Document = (function () {
     _cleanUp() {
       // if we arrive here from _onError the pending and inflight ops will have been cleared
       if (this.hasBufferedOps()) {
-        sl_console.log(
+        debugConsole.log(
           `[_cleanUp] Document (${this.doc_id}) has buffered ops, refusing to remove from openDocs`
         )
         return // return immediately, do not unbind from events
       } else if (Document.openDocs[this.doc_id] === this) {
-        sl_console.log(
+        debugConsole.log(
           `[_cleanUp] Removing self (${this.doc_id}) from in openDocs`
         )
         delete Document.openDocs[this.doc_id]
@@ -626,7 +627,7 @@ export default Document = (function () {
         // It's possible that this instance has error, and the doc has been reloaded.
         // This creates a new instance in Document.openDoc with the same id. We shouldn't
         // clear it because it's not this instance.
-        sl_console.log(
+        debugConsole.log(
           `[_cleanUp] New instance of (${this.doc_id}) created. Not removing`
         )
       }
@@ -716,9 +717,9 @@ export default Document = (function () {
         meta = {}
       }
       meta.doc_id = this.doc_id
-      sl_console.log('ShareJS error', error, meta)
+      debugConsole.log('ShareJS error', error, meta)
       if (error.message === 'no project_id found on client') {
-        sl_console.log('ignoring error, will wait to join project')
+        debugConsole.log('ignoring error, will wait to join project')
         return
       }
       if (this.doc != null) {

+ 10 - 9
services/web/frontend/js/ide/editor/EditorManager.js

@@ -24,6 +24,7 @@ import './controllers/CompileButton'
 import './controllers/SwitchToPDFButton'
 import getMeta from '../../utils/meta'
 import { hasSeenCM6SwitchAwaySurvey } from '../../features/source-editor/utils/switch-away-survey'
+import { debugConsole } from '@/utils/debugging'
 
 let EditorManager
 
@@ -120,7 +121,7 @@ export default EditorManager = (function () {
         //  sync: we can use any new version of the doc that the server may
         //  present us. There should be no need to insert local changes into
         //  the doc history as the user comes back.
-        sl_console.log('[EditorManager] forcing flush onblur')
+        debugConsole.log('[EditorManager] forcing flush onblur')
         Document.flushAll()
       })
 
@@ -237,7 +238,7 @@ export default EditorManager = (function () {
       if (options == null) {
         options = {}
       }
-      sl_console.log(`[openDoc] Opening ${doc.id}`)
+      debugConsole.log(`[openDoc] Opening ${doc.id}`)
       if (this.$scope.ui.view === 'editor') {
         // store position of previous doc before switching docs
         this.$scope.$broadcast('store-doc-position')
@@ -292,7 +293,7 @@ export default EditorManager = (function () {
         this.$scope.editor.opening = true
         return this._openNewDocument(doc, (error, sharejs_doc) => {
           if (error && error.message === 'another document was loaded') {
-            sl_console.log(
+            debugConsole.log(
               `[openDoc] another document was loaded while ${doc.id} was loading`
             )
             return
@@ -330,7 +331,7 @@ export default EditorManager = (function () {
         current_sharejs_doc && current_sharejs_doc.hasBufferedOps()
       const changingDoc = current_sharejs_doc && currentDocId !== doc.id
       if (changingDoc || hasBufferedOps) {
-        sl_console.log('[_openNewDocument] Leaving existing open doc...')
+        debugConsole.log('[_openNewDocument] Leaving existing open doc...')
 
         // Do not trigger any UI changes from remote operations
         this._unbindFromDocumentEvents(current_sharejs_doc)
@@ -344,14 +345,14 @@ export default EditorManager = (function () {
         const editorOpenDocEpoch = ++this.editorOpenDocEpoch
         current_sharejs_doc.leaveAndCleanUp(error => {
           if (error) {
-            sl_console.log(
+            debugConsole.log(
               `[_openNewDocument] error leaving doc ${currentDocId}`,
               error
             )
             return callback(error)
           }
           if (this.editorOpenDocEpoch !== editorOpenDocEpoch) {
-            sl_console.log(
+            debugConsole.log(
               `[openNewDocument] editorOpenDocEpoch mismatch ${this.editorOpenDocEpoch} vs ${editorOpenDocEpoch}`
             )
             return callback(new Error('another document was loaded'))
@@ -367,19 +368,19 @@ export default EditorManager = (function () {
       if (callback == null) {
         callback = function () {}
       }
-      sl_console.log('[_doOpenNewDocument] Opening...')
+      debugConsole.log('[_doOpenNewDocument] Opening...')
       const new_sharejs_doc = Document.getDocument(this.ide, doc.id)
       const editorOpenDocEpoch = ++this.editorOpenDocEpoch
       return new_sharejs_doc.join(error => {
         if (error != null) {
-          sl_console.log(
+          debugConsole.log(
             `[_doOpenNewDocument] error joining doc ${doc.id}`,
             error
           )
           return callback(error)
         }
         if (this.editorOpenDocEpoch !== editorOpenDocEpoch) {
-          sl_console.log(
+          debugConsole.log(
             `[openNewDocument] editorOpenDocEpoch mismatch ${this.editorOpenDocEpoch} vs ${editorOpenDocEpoch}`
           )
           new_sharejs_doc.leaveAndCleanUp()

+ 15 - 12
services/web/frontend/js/ide/editor/ShareJsDoc.js

@@ -19,6 +19,7 @@
 import EventEmitter from '../../utils/EventEmitter'
 import ShareJs from '../../vendor/libs/sharejs'
 import EditorWatchdogManager from '../connection/EditorWatchdogManager'
+import { debugConsole } from '@/utils/debugging'
 
 let ShareJsDoc
 const SINGLE_USER_FLUSH_DELAY = 2000 // ms
@@ -59,14 +60,14 @@ export default ShareJsDoc = (function () {
             window.disconnectOnUpdate != null &&
             Math.random() < window.disconnectOnUpdate
           ) {
-            sl_console.log('Disconnecting on update', update)
+            debugConsole.log('Disconnecting on update', update)
             window._ide.socket.socket.disconnect()
           }
           if (
             window.dropUpdates != null &&
             Math.random() < window.dropUpdates
           ) {
-            sl_console.log('Simulating a lost update', update)
+            debugConsole.log('Simulating a lost update', update)
             return
           }
           if (this.track_changes) {
@@ -141,7 +142,7 @@ export default ShareJsDoc = (function () {
       })
       let nextPos
       while ((nextPos = doc.snapshot.indexOf('\r')) !== -1) {
-        sl_console.log('[ShareJsDoc] remove-carriage-return-char', nextPos)
+        debugConsole.log('[ShareJsDoc] remove-carriage-return-char', nextPos)
         doc.del(nextPos, 1)
       }
     }
@@ -159,11 +160,13 @@ export default ShareJsDoc = (function () {
     }
 
     _pushOntoQueue(message) {
-      sl_console.log(`[processUpdate] push onto queue ${message.v}`)
+      debugConsole.log(`[processUpdate] push onto queue ${message.v}`)
       // set a timer so that we never leave messages in the queue indefinitely
       if (!this.queuedMessageTimer) {
         this.queuedMessageTimer = setTimeout(() => {
-          sl_console.log(`[processUpdate] queue timeout fired for ${message.v}`)
+          debugConsole.log(
+            `[processUpdate] queue timeout fired for ${message.v}`
+          )
           // force the message to be processed after the timeout,
           // it will cause an error if the missing update has not arrived
           this.processUpdateFromServer(message)
@@ -187,13 +190,13 @@ export default ShareJsDoc = (function () {
           // there are updates we still can't apply yet
         } else {
           // there's a version we can accept on the queue, apply it
-          sl_console.log(
+          debugConsole.log(
             `[processUpdate] taken from queue ${nextAvailableVersion}`
           )
           this.processUpdateFromServerInOrder(this.queuedMessages.shift())
           // clear the pending timer if the queue has now been cleared
           if (this.queuedMessages.length === 0 && this.queuedMessageTimer) {
-            sl_console.log('[processUpdate] queue is empty, cleared timeout')
+            debugConsole.log('[processUpdate] queue is empty, cleared timeout')
             clearTimeout(this.queuedMessageTimer)
             this.queuedMessageTimer = null
           }
@@ -235,7 +238,7 @@ export default ShareJsDoc = (function () {
         this._doc._onMessage(message)
       } catch (error) {
         // Version mismatches are thrown as errors
-        console.log(error)
+        debugConsole.error(error)
         this._handleError(error)
         return error // return the error for queue handling
       }
@@ -288,7 +291,7 @@ export default ShareJsDoc = (function () {
     }
 
     updateConnectionState(state) {
-      sl_console.log(`[updateConnectionState] Setting state to ${state}`)
+      debugConsole.log(`[updateConnectionState] Setting state to ${state}`)
       this.connection.state = state
       this.connection.id = this.socket.publicId
       this._doc.autoOpen = false
@@ -385,7 +388,7 @@ export default ShareJsDoc = (function () {
         // Only send the update again if inflightOp is still populated
         // This can be cleared when hard reloading the document in which
         // case we don't want to keep trying to send it.
-        sl_console.log('[inflightOpTimeout] Trying op again')
+        debugConsole.log('[inflightOpTimeout] Trying op again')
         if (this._doc.inflightOp != null) {
           // When there is a socket.io disconnect, @_doc.inflightSubmittedIds
           // is updated with the socket.io client id of the current op in flight
@@ -403,7 +406,7 @@ export default ShareJsDoc = (function () {
           // when we've joined the project
           if (this.connection.state !== 'ok') {
             let timer
-            sl_console.log(
+            debugConsole.log(
               '[inflightOpTimeout] Not connected, retrying in 0.5s'
             )
             return (timer = setTimeout(
@@ -411,7 +414,7 @@ export default ShareJsDoc = (function () {
               this.WAIT_FOR_CONNECTION_TIMEOUT
             ))
           } else {
-            sl_console.log('[inflightOpTimeout] Sending')
+            debugConsole.log('[inflightOpTimeout] Sending')
             return this.connection.send(update)
           }
         }

+ 2 - 1
services/web/frontend/js/ide/editor/ace-performance.ts

@@ -1,6 +1,7 @@
 import { round } from 'lodash'
 import grammarlyExtensionPresent from '../../shared/utils/grammarly'
 import getMeta from '../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 const TIMER_DOM_UPDATE_NAME = 'Ace-DomUpdate'
 const TIMER_MEASURE_NAME = 'Ace-Keypress-Measure'
@@ -154,5 +155,5 @@ export function reportAcePerf() {
 }
 
 window._reportAcePerf = () => {
-  console.log(reportAcePerf())
+  debugConsole.warn(reportAcePerf())
 }

+ 2 - 1
services/web/frontend/js/ide/editor/directives/aceEditor/spell-check/SpellCheckManager.js

@@ -1,4 +1,5 @@
 import ignoredWords from '../../../../../features/dictionary/ignored-words'
+import { debugConsole } from '@/utils/debugging'
 
 // eslint-disable-next-line prefer-regex-literals
 const BLACKLISTED_COMMAND_REGEX = new RegExp(
@@ -352,7 +353,7 @@ class SpellCheckManager {
     if (callback == null) {
       callback = function (error, result) {
         if (error) {
-          console.error(error)
+          debugConsole.error(error)
         }
       }
     }

+ 3 - 2
services/web/frontend/js/ide/editor/directives/aceEditor/track-changes/TrackChangesManager.js

@@ -6,6 +6,7 @@ import EditorShareJsCodec from '../../../EditorShareJsCodec'
 import 'ace/ace'
 import '../../../../../utils/EventEmitter'
 import '../../../../colors/ColorManager'
+import { debugConsole } from '@/utils/debugging'
 const { Range } = ace.require('ace/range')
 class TrackChangesManager {
   constructor($scope, editor, element, adapter) {
@@ -547,7 +548,7 @@ class TrackChangesManager {
         marker.range.end.row !== end.row ||
         marker.range.end.column !== end.column
       ) {
-        console.error("Change doesn't match marker anymore", {
+        debugConsole.error("Change doesn't match marker anymore", {
           marker,
           start,
           end,
@@ -560,7 +561,7 @@ class TrackChangesManager {
       for (const marker_id in markers) {
         marker = markers[marker_id]
         if (/track-changes/.test(marker.clazz)) {
-          result.push(console.error('Orphaned ace marker', marker))
+          result.push(debugConsole.error('Orphaned ace marker', marker))
         } else {
           result.push(undefined)
         }

+ 2 - 1
services/web/frontend/js/ide/file-tree/FileTreeManager.js

@@ -23,6 +23,7 @@ import './controllers/FileTreeController'
 import './controllers/FileTreeEntityController'
 import './controllers/FileTreeFolderController'
 import '../../features/file-tree/controllers/file-tree-controller'
+import { debugConsole } from '@/utils/debugging'
 let FileTreeManager
 
 export default FileTreeManager = class FileTreeManager {
@@ -631,7 +632,7 @@ export default FileTreeManager = class FileTreeManager {
     const provider =
       file.linkedFileData != null ? file.linkedFileData.provider : undefined
     if (provider == null) {
-      console.warn(`>> no provider for ${file.name}`, file)
+      debugConsole.warn(`>> no provider for ${file.name}`, file)
       return
     }
     return this.ide.$http.post(

+ 2 - 1
services/web/frontend/js/ide/history/controllers/HistoryV2ToolbarController.js

@@ -11,6 +11,7 @@
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
 import App from '../../../base'
+import { debugConsole } from '@/utils/debugging'
 
 export default App.controller(
   'HistoryV2ToolbarController',
@@ -117,7 +118,7 @@ export default App.controller(
             }, 0)
           }
         })
-        .catch(err => console.warn(err))
+        .catch(debugConsole.error)
     }
   }
 )

+ 0 - 1
services/web/frontend/js/ide/references/ReferencesManager.js

@@ -59,7 +59,6 @@ export default ReferencesManager = class ReferencesManager {
   }
 
   _storeReferencesKeys(newKeys) {
-    // console.log '>> storing references keys'
     const oldKeys = this.$scope.$root._references.keys
     const keys = _.union(oldKeys, newKeys)
     window.dispatchEvent(

+ 4 - 4
services/web/frontend/js/ide/review-panel/directives/reviewPanelSorted.js

@@ -14,6 +14,7 @@
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
 import App from '../../../base'
+import { debugConsole } from '@/utils/debugging'
 
 export default App.directive('reviewPanelSorted', $timeout => ({
   link(scope, element, attrs) {
@@ -109,7 +110,7 @@ export default App.directive('reviewPanelSorted', $timeout => ({
         : []
       previous_focused_entry_index = focused_entry_index
 
-      sl_console.log('focused_entry_index', focused_entry_index)
+      debugConsole.log('focused_entry_index', focused_entry_index)
 
       const positionLayoutEl = function (
         $callout_el,
@@ -180,7 +181,7 @@ export default App.directive('reviewPanelSorted', $timeout => ({
             screenPosHeight
           )
         }
-        sl_console.log('ENTRY', { entry: entry.scope.entry, top })
+        debugConsole.log('ENTRY', { entry: entry.scope.entry, top })
       }
 
       let previousTop = focused_entry_top
@@ -210,7 +211,7 @@ export default App.directive('reviewPanelSorted', $timeout => ({
             screenPosHeight
           )
         }
-        sl_console.log('ENTRY', { entry: entry.scope.entry, top })
+        debugConsole.log('ENTRY', { entry: entry.scope.entry, top })
       }
 
       const lastTop = top
@@ -268,7 +269,6 @@ export default App.directive('reviewPanelSorted', $timeout => ({
         return (ignoreNextAceEvent = false)
       } else {
         list.height(height)
-        // console.log({height, scrollTop, top: height - scrollTop})
         return list.css({ top: -scrollTop })
       }
     }

+ 2 - 1
services/web/frontend/js/ide/services/ide.js

@@ -14,6 +14,7 @@
  */
 import App from '../../base'
 import EditorWatchdogManager from '../connection/EditorWatchdogManager'
+import { debugConsole } from '@/utils/debugging'
 // We create and provide this as service so that we can access the global ide
 // from within other parts of the angular app.
 App.factory(
@@ -38,7 +39,7 @@ App.factory(
       if (meta == null) {
         meta = {}
       }
-      sl_console.log('event', type, meta)
+      debugConsole.log('event', type, meta)
       this.recentEvents.push({ type, meta, date: new Date() })
       if (this.recentEvents.length > 100) {
         return this.recentEvents.shift()

+ 2 - 1
services/web/frontend/js/infrastructure/cm6-performance.ts

@@ -3,6 +3,7 @@ import { EditorView } from '@codemirror/view'
 import { round } from 'lodash'
 import grammarlyExtensionPresent from '../shared/utils/grammarly'
 import getMeta from '../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 const TIMER_START_NAME = 'CM6-BeforeUpdate'
 const TIMER_END_NAME = 'CM6-AfterUpdate'
@@ -326,5 +327,5 @@ export function reportCM6Perf() {
 }
 
 window._reportCM6Perf = () => {
-  console.log(reportCM6Perf())
+  debugConsole.warn(reportCM6Perf())
 }

+ 4 - 3
services/web/frontend/js/infrastructure/error-reporter.js

@@ -1,6 +1,7 @@
 // Conditionally enable Sentry based on whether the DSN token is set
 import getMeta from '../utils/meta'
 import OError from '@overleaf/o-error'
+import { debugConsole } from '@/utils/debugging'
 
 const reporterPromise = window.ExposedSettings?.sentryDsn
   ? sentryReporter()
@@ -81,7 +82,7 @@ function sentryReporter() {
       })
       // If Sentry fails to load, use the null reporter instead
       .catch(error => {
-        console.error(error)
+        debugConsole.error(error)
         return nullReporter()
       })
   )
@@ -89,8 +90,8 @@ function sentryReporter() {
 
 function nullReporter() {
   return Promise.resolve({
-    captureException: console.error,
-    captureMessage: console.error,
+    captureException: debugConsole.error,
+    captureMessage: debugConsole.error,
   })
 }
 

+ 3 - 1
services/web/frontend/js/infrastructure/local-storage.js

@@ -3,6 +3,8 @@
  * use localStorage for anything critical, so in that case just fail gracefully.
  */
 
+import { debugConsole } from '@/utils/debugging'
+
 /**
  * Catch, log and otherwise ignore errors.
  *
@@ -14,7 +16,7 @@ const callSafe = function (fn, key, value) {
   try {
     return fn(key, value)
   } catch (e) {
-    console.error('localStorage exception', e)
+    debugConsole.error('localStorage exception', e)
     return null
   }
 }

+ 3 - 1
services/web/frontend/js/infrastructure/session-storage.js

@@ -3,6 +3,8 @@
  * We don't use sessionStorage for anything critical, so in that case just fail gracefully.
  */
 
+import { debugConsole } from '@/utils/debugging'
+
 /**
  * Catch, log and otherwise ignore errors.
  *
@@ -14,7 +16,7 @@ const callSafe = function (fn, key, value) {
   try {
     return fn(key, value)
   } catch (e) {
-    console.error('sessionStorage exception', e)
+    debugConsole.error('sessionStorage exception', e)
     return null
   }
 }

+ 2 - 1
services/web/frontend/js/main.js

@@ -41,6 +41,7 @@ import './filters/formatDate'
 import './features/cookie-banner'
 import '../../modules/modules-main.js'
 import './cdn-load-test'
+import { debugConsole } from '@/utils/debugging'
 angular.module('SharelatexApp').config(function ($locationProvider) {
   try {
     return $locationProvider.html5Mode({
@@ -49,7 +50,7 @@ angular.module('SharelatexApp').config(function ($locationProvider) {
       rewriteLinks: false,
     })
   } catch (e) {
-    return console.error("Error while trying to fix '#' links: ", e)
+    debugConsole.error("Error while trying to fix '#' links: ", e)
   }
 })
 export default angular.bootstrap(document.body, ['SharelatexApp'])

+ 4 - 1
services/web/frontend/js/main/annual-upgrade.js

@@ -10,6 +10,7 @@
  * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  */
 import App from '../base'
+import { debugConsole } from '@/utils/debugging'
 
 export default App.controller(
   'AnnualUpgradeController',
@@ -38,7 +39,9 @@ export default App.controller(
       return $http
         .post(MESSAGES_URL, body)
         .then(() => ($scope.upgradeComplete = true))
-        .catch(() => console.log('something went wrong changing plan'))
+        .catch(err =>
+          debugConsole.error('something went wrong changing plan', err)
+        )
     })
   }
 )

+ 0 - 1
services/web/frontend/js/main/clear-sessions.js

@@ -22,7 +22,6 @@ export default App.controller(
     }
 
     return ($scope.clearSessions = function () {
-      console.log('>> clearing all sessions')
       return $http({
         method: 'POST',
         url: '/user/sessions/clear',

+ 3 - 2
services/web/frontend/js/main/event.js

@@ -16,6 +16,7 @@ import moment from 'moment'
 import App from '../base'
 import '../modules/localStorage'
 import { sendMB } from '../infrastructure/event-tracking'
+import { debugConsole } from '@/utils/debugging'
 const CACHE_KEY = 'mbEvents'
 
 // keep track of how many heartbeats we've sent so we can calculate how
@@ -71,14 +72,14 @@ App.factory('eventTracking', function ($http, localStorage) {
     },
 
     editingSessionHeartbeat(segmentationCb = () => {}) {
-      sl_console.log('[Event] heartbeat trigger')
+      debugConsole.log('[Event] heartbeat trigger')
 
       // If the next heartbeat is in the future, stop
       if (nextHeartbeat > new Date()) return
 
       const segmentation = segmentationCb()
 
-      sl_console.log('[Event] send heartbeat request', segmentation)
+      debugConsole.log('[Event] send heartbeat request', segmentation)
       _sendEditingSessionHeartbeat(segmentation)
 
       heartbeatsSent++

+ 3 - 2
services/web/frontend/js/main/subscription/team-invite-controller.js

@@ -11,6 +11,7 @@
  */
 import App from '../../base'
 import getMeta from '../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 export default App.controller('TeamInviteController', function ($scope, $http) {
   $scope.inflight = false
@@ -42,7 +43,7 @@ export default App.controller('TeamInviteController', function ($scope, $http) {
     return request.catch(() => {
       $scope.inflight = false
       $scope.cancel_error = true
-      console.log('the request failed')
+      debugConsole.error('the request failed')
     })
   }
 
@@ -61,6 +62,6 @@ export default App.controller('TeamInviteController', function ($scope, $http) {
         return ($scope.requestSent = false)
       }
     })
-    return request.catch(() => console.log('the request failed'))
+    return request.catch(() => debugConsole.error('the request failed'))
   })
 })

+ 4 - 3
services/web/frontend/js/main/token-access.js

@@ -1,4 +1,5 @@
 import App from '../base'
+import { debugConsole } from '@/utils/debugging'
 App.controller(
   'TokenAccessPageController',
   ($scope, $http, $location, localStorage) => {
@@ -56,7 +57,7 @@ App.controller(
           if (data.redirect) {
             const redirect = response.data.redirect
             if (!redirect) {
-              console.warn(
+              debugConsole.warn(
                 'no redirect supplied in success response data',
                 response
               )
@@ -71,7 +72,7 @@ App.controller(
             $scope.mode = 'requireAccept'
             $scope.requireAccept = data.requireAccept
           } else {
-            console.warn(
+            debugConsole.warn(
               'invalid data from server in success response',
               response
             )
@@ -79,7 +80,7 @@ App.controller(
           }
         },
         function errorCallback(response) {
-          console.warn('error response from server', response)
+          debugConsole.warn('error response from server', response)
           $scope.accessInFlight = false
           $scope.accessError = response.status === 404 ? 'not_found' : 'error'
         }

+ 3 - 1
services/web/frontend/js/modules/localStorage.js

@@ -1,3 +1,5 @@
+import { debugConsole } from '@/utils/debugging'
+
 angular.module('localStorage', []).value('localStorage', localStorage)
 
 /*
@@ -9,7 +11,7 @@ function localStorage(...args) {
   try {
     return $.localStorage(...args)
   } catch (e) {
-    console.error('localStorage exception', e)
+    debugConsole.error('localStorage exception', e)
     return null
   }
 }

+ 3 - 1
services/web/frontend/js/modules/sessionStorage.js

@@ -1,3 +1,5 @@
+import { debugConsole } from '@/utils/debugging'
+
 angular.module('sessionStorage', []).value('sessionStorage', sessionStorage)
 
 /*
@@ -9,7 +11,7 @@ function sessionStorage(...args) {
   try {
     return $.sessionStorage(...args)
   } catch (e) {
-    console.error('sessionStorage exception', e)
+    debugConsole.error('sessionStorage exception', e)
     return null
   }
 }

+ 5 - 4
services/web/frontend/js/shared/context/detach-context.js

@@ -10,6 +10,7 @@ import PropTypes from 'prop-types'
 import getMeta from '../../utils/meta'
 import { buildUrlWithDetachRole } from '../utils/url-helper'
 import useCallbackHandlers from '../hooks/use-callback-handlers'
+import { debugConsole } from '@/utils/debugging'
 
 export const DetachContext = createContext()
 
@@ -43,7 +44,7 @@ export function DetachProvider({ children }) {
 
   useEffect(() => {
     if (debugPdfDetach) {
-      console.log('Effect', { role })
+      debugConsole.warn('Effect', { role })
     }
     window.history.replaceState({}, '', buildUrlWithDetachRole(role).toString())
   }, [role])
@@ -52,7 +53,7 @@ export function DetachProvider({ children }) {
     if (detachChannel) {
       const listener = event => {
         if (debugPdfDetach) {
-          console.log(`Receiving:`, event.data)
+          debugConsole.warn(`Receiving:`, event.data)
         }
         callEventHandlers(event.data)
       }
@@ -69,7 +70,7 @@ export function DetachProvider({ children }) {
     (event, data) => {
       if (!role) {
         if (debugPdfDetach) {
-          console.log('Not Broadcasting (no role)', {
+          debugConsole.warn('Not Broadcasting (no role)', {
             role,
             event,
             data,
@@ -78,7 +79,7 @@ export function DetachProvider({ children }) {
         return
       }
       if (debugPdfDetach) {
-        console.log('Broadcasting', {
+        debugConsole.warn('Broadcasting', {
           role,
           event,
           data,

+ 2 - 1
services/web/frontend/js/shared/context/layout-context.js

@@ -11,6 +11,7 @@ import useDetachLayout from '../hooks/use-detach-layout'
 import { useIdeContext } from './ide-context'
 import localStorage from '../../infrastructure/local-storage'
 import getMeta from '../../utils/meta'
+import { debugConsole } from '@/utils/debugging'
 
 const debugPdfDetach = getMeta('ol-debugPdfDetach')
 
@@ -122,7 +123,7 @@ export function LayoutProvider({ children }) {
 
   useEffect(() => {
     if (debugPdfDetach) {
-      console.log('Layout Effect', {
+      debugConsole.warn('Layout Effect', {
         detachIsRedundant,
         detachRole,
         detachIsLinking,

Некоторые файлы не были показаны из-за большого количества измененных файлов