codex-overleaf-link 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -314
- package/README.zh-CN.md +287 -329
- package/extension/bootstrap/background.js +47 -10
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +10 -16
- package/extension/bootstrap/update.js +25 -2
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +28 -3
- package/extension/src/backgroundSharedSessions.js +380 -0
- package/extension/src/backgroundUpdateCoordinator.js +442 -379
- package/extension/src/content/activeTurnControl.js +8 -0
- package/extension/src/content/assetTransferBroker.js +186 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +24 -22
- package/extension/src/content/composerPanel.js +23 -8
- package/extension/src/content/contentRuntime.js +507 -507
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diagnosticsExport.js +222 -0
- package/extension/src/content/diagnosticsPanel.js +48 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/generated/content.bundle.js +8330 -1349
- package/extension/src/content/generated/content.bundle.meta.json +22 -3
- package/extension/src/content/legacyGlobalRegistry.js +19 -1
- package/extension/src/content/localSkillsPanel.js +42 -7
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/pageBridgeClient.js +18 -3
- package/extension/src/content/panelMaintenance.js +48 -121
- package/extension/src/content/panelRenderer.js +4 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/providerSettingsCoordinator.js +17 -6
- package/extension/src/content/providerSettingsDialog.js +661 -108
- package/extension/src/content/recentProjects.js +10 -1
- package/extension/src/content/runActivitySummary.js +631 -0
- package/extension/src/content/runController.js +3 -1
- package/extension/src/content/runFailureNotice.js +14 -1
- package/extension/src/content/runGuidanceController.js +4 -3
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +2 -1
- package/extension/src/content/runResultActions.js +123 -3
- package/extension/src/content/runTimelineView.js +186 -154
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sessionPersistence.js +6 -1
- package/extension/src/content/settingsPanel.js +77 -52
- package/extension/src/content/settingsWorkbench.js +374 -0
- package/extension/src/content/sharedSessionViewSync.js +72 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateNotice.js +121 -50
- package/extension/src/content/writebackController.js +257 -0
- package/extension/src/content/writebackOrchestrator.js +146 -141
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +576 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +97 -19
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/referenceProjects.js +148 -0
- package/extension/src/page/saveState.js +14 -2
- package/extension/src/page/snapshotRouter.js +44 -13
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +350 -59
- package/extension/src/page/trackedChangeCapture.js +42 -56
- package/extension/src/page/trackedChangeOwnership.js +198 -0
- package/extension/src/page/trackedChangeReplay.js +164 -0
- package/extension/src/page/trackedChangesLifecycle.js +135 -149
- package/extension/src/page/treeOperations.js +26 -2
- package/extension/src/page/writebackReceiptJournal.js +112 -0
- package/extension/src/page/writebackRouter.js +175 -135
- package/extension/src/pageBridge.js +56 -101
- package/extension/src/popup.js +203 -59
- package/extension/src/shared/agentTranscript.js +18 -4
- package/extension/src/shared/auditRecords.js +71 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/compileAdapter.js +42 -0
- package/extension/src/shared/failureReasons.js +20 -0
- package/extension/src/shared/globalPreferences.js +1 -1
- package/extension/src/shared/i18n.js +93 -25
- package/extension/src/shared/managedUpdateProjection.js +6 -3
- package/extension/src/shared/pageRpcContract.js +7 -1
- package/extension/src/shared/providerProfiles.js +109 -0
- package/extension/src/shared/runActivityModel.js +361 -0
- package/extension/src/shared/runExecutionSnapshot.js +8 -2
- package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +28 -0
- package/extension/src/shared/sessionState.js +97 -6
- package/extension/src/shared/sharedSessionBridge.js +288 -0
- package/extension/src/shared/storageDb.js +22 -55
- package/extension/src/shared/storageMigration.js +3 -0
- package/extension/src/shared/storageRunActions.js +10 -4
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +87 -0
- package/extension/src/shared/updateConsent.js +37 -8
- package/extension/src/shared/writebackIntent.js +62 -0
- package/extension/src/shared/writebackSettlement.js +29 -10
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +1972 -384
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +2 -1
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +2 -0
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +106 -140
- package/native-host/src/codexSessionTiming.js +3 -0
- package/native-host/src/managedInstall.js +5 -4
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +13 -1
- package/native-host/src/providerReasoning.js +9 -5
- package/native-host/src/providerStore.js +4 -0
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/selectionScope.js +58 -0
- package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
- package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
- package/native-host/src/subagentBroker.js +102 -98
- package/native-host/src/subagentTelemetry.js +316 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +31 -0
- package/native-host/src/taskRunnerRuntime.js +55 -5
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +122 -12
- package/native-host/src/updateNetworkControl.js +75 -0
- package/native-host/src/updateTrust.js +21 -2
- package/native-host/src/vendor/pdfjs/LICENSE +177 -0
- package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
- package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/native-host/src/writingStyleEvidence.js +102 -0
- package/native-host/src/writingStylePdfWorker.js +35 -0
- package/native-host/src/writingStyleProfile.js +165 -0
- package/native-host/src/writingStyleRuntime.js +447 -0
- package/package.json +1 -1
|
@@ -24,14 +24,18 @@
|
|
|
24
24
|
const OVERLEAF_MATCHES = [
|
|
25
25
|
'https://www.overleaf.com/project',
|
|
26
26
|
'https://overleaf.com/project',
|
|
27
|
+
'https://cn.overleaf.com/project',
|
|
27
28
|
'https://www.overleaf.com/project/*',
|
|
28
|
-
'https://overleaf.com/project/*'
|
|
29
|
+
'https://overleaf.com/project/*',
|
|
30
|
+
'https://cn.overleaf.com/project/*'
|
|
29
31
|
];
|
|
30
32
|
const MESSAGE_TYPES = new Set([
|
|
31
33
|
'codex-overleaf/consent-update-get-state',
|
|
32
34
|
'codex-overleaf/consent-update-check',
|
|
33
35
|
'codex-overleaf/consent-update-install',
|
|
34
36
|
'codex-overleaf/consent-update-later',
|
|
37
|
+
'codex-overleaf/consent-update-cancel',
|
|
38
|
+
'codex-overleaf/consent-update-recover',
|
|
35
39
|
'codex-overleaf/consent-update-dismiss',
|
|
36
40
|
'codex-overleaf/consent-update-reload'
|
|
37
41
|
]);
|
|
@@ -47,6 +51,11 @@
|
|
|
47
51
|
let runtimeStatusAt = 0;
|
|
48
52
|
let runtimeStatusRead = null;
|
|
49
53
|
let runtimeReloadMessage = '';
|
|
54
|
+
let operationGeneration = 0;
|
|
55
|
+
let activeOperation = null;
|
|
56
|
+
let cancellationPromise = null;
|
|
57
|
+
let recoveryPromise = null;
|
|
58
|
+
let stateWriteTail = Promise.resolve();
|
|
50
59
|
|
|
51
60
|
function init(options = {}) {
|
|
52
61
|
if (initialized || !policy || !revocation) return;
|
|
@@ -59,7 +68,10 @@
|
|
|
59
68
|
sendResponse({ ok: false, error: { code: 'forbidden_sender', message: 'Update actions are limited to this extension and Overleaf project tabs.' } });
|
|
60
69
|
return false;
|
|
61
70
|
}
|
|
62
|
-
|
|
71
|
+
const direct = ['codex-overleaf/consent-update-get-state',
|
|
72
|
+
'codex-overleaf/consent-update-cancel', 'codex-overleaf/consent-update-later',
|
|
73
|
+
'codex-overleaf/consent-update-recover'].includes(message.type);
|
|
74
|
+
(direct ? handleMessage(message) : enqueuePolicyAction(() => handleMessage(message)))
|
|
63
75
|
.then(result => sendResponse({ ok: true, result }))
|
|
64
76
|
.catch(error => sendResponse({ ok: false, error: safeError(error) }));
|
|
65
77
|
return true;
|
|
@@ -70,7 +82,7 @@
|
|
|
70
82
|
void enqueuePolicyAction(() => checkOnly({ manual: false })).catch(() => {});
|
|
71
83
|
}
|
|
72
84
|
if (alarm?.name === WATCHDOG_ALARM) {
|
|
73
|
-
void
|
|
85
|
+
void reconcileRecoveryState().catch(() => {});
|
|
74
86
|
}
|
|
75
87
|
if (alarm?.name === IDLE_ALARM) {
|
|
76
88
|
void enqueuePolicyAction(() => tryActivateStagedUpdate()).catch(() => {});
|
|
@@ -128,11 +140,16 @@
|
|
|
128
140
|
switch (message.type) {
|
|
129
141
|
case 'codex-overleaf/consent-update-get-state':
|
|
130
142
|
return getView();
|
|
143
|
+
case 'codex-overleaf/consent-update-recover':
|
|
144
|
+
await reconcileRecoveryState();
|
|
145
|
+
return getView();
|
|
131
146
|
case 'codex-overleaf/consent-update-check':
|
|
132
147
|
return checkOnly({ manual: true });
|
|
133
148
|
case 'codex-overleaf/consent-update-install':
|
|
134
149
|
return installUpdate();
|
|
135
150
|
case 'codex-overleaf/consent-update-later':
|
|
151
|
+
return snoozeManualInstall().then(view => view || postponeUpdate());
|
|
152
|
+
case 'codex-overleaf/consent-update-cancel':
|
|
136
153
|
return postponeUpdate();
|
|
137
154
|
case 'codex-overleaf/consent-update-dismiss':
|
|
138
155
|
return dismissCompletedUpdate();
|
|
@@ -143,106 +160,83 @@
|
|
|
143
160
|
}
|
|
144
161
|
}
|
|
145
162
|
|
|
146
|
-
async function checkOnly({ manual }) {
|
|
147
|
-
const
|
|
148
|
-
if (consent.authorizedVersion || policy.isExecutionState(currentState.state)) {
|
|
149
|
-
return getView();
|
|
150
|
-
}
|
|
151
|
-
await setUpdateState({
|
|
152
|
-
...currentState,
|
|
153
|
-
state: 'checking',
|
|
154
|
-
initiatedBy: manual ? 'manual' : 'automatic',
|
|
155
|
-
code: '',
|
|
156
|
-
message: '',
|
|
157
|
-
blocker: '',
|
|
158
|
-
blockers: []
|
|
159
|
-
});
|
|
160
|
-
|
|
163
|
+
async function checkOnly({ manual }, inheritedOperation) {
|
|
164
|
+
const operation = inheritedOperation || beginOperation();
|
|
161
165
|
try {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
code:
|
|
213
|
-
|
|
214
|
-
|
|
166
|
+
const [currentState, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
167
|
+
assertOperation(operation);
|
|
168
|
+
if (currentState.cancelRequested || currentState.recoveryPending
|
|
169
|
+
|| consent.authorizedVersion || policy.isExecutionState(currentState.state)) return getView();
|
|
170
|
+
operation.phase = 'checking';
|
|
171
|
+
await setUpdateState({ ...currentState, state: 'checking', operationId: operation.id,
|
|
172
|
+
cancelRequested: false, initiatedBy: manual ? 'manual' : 'automatic',
|
|
173
|
+
code: '', message: '', blocker: '', blockers: [], postponeUntil: LEGACY_GUARD }, { operation });
|
|
174
|
+
try {
|
|
175
|
+
const result = await withTimeout(requestNative('update.check', {
|
|
176
|
+
operationId: operation.id, currentVersion: currentVersion(), etag: currentState.etag || ''
|
|
177
|
+
}), CHECK_REQUEST_TIMEOUT_MS, codedError('update_check_timeout', 'The managed update check did not complete in time.'));
|
|
178
|
+
assertOperation(operation);
|
|
179
|
+
const checkedAt = Date.now();
|
|
180
|
+
if (result.available || (result.reason === 'not_modified'
|
|
181
|
+
&& policy.compareStableVersions(currentState.latestVersion, currentVersion()) > 0)) {
|
|
182
|
+
const latestVersion = result.latestVersion || currentState.latestVersion;
|
|
183
|
+
await setConsentState({ ...consent,
|
|
184
|
+
...(latestVersion !== consent.snoozedVersion ? { snoozedVersion: '', snoozedUntil: 0 } : {}),
|
|
185
|
+
lastPromptedVersion: latestVersion,
|
|
186
|
+
lastPromptedAt: manual ? checkedAt : consent.lastPromptedAt
|
|
187
|
+
}, { operation });
|
|
188
|
+
await setUpdateState({ ...currentState, state: 'update_available', operationId: operation.id,
|
|
189
|
+
currentVersion: result.currentVersion || currentVersion(), latestVersion,
|
|
190
|
+
managed: true, etag: result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
191
|
+
postponeUntil: LEGACY_GUARD, code: '', message: '', cancelRequested: false
|
|
192
|
+
}, { operation });
|
|
193
|
+
} else if (result.reason === 'manual_install_required'
|
|
194
|
+
&& policy.compareStableVersions(result.latestVersion, currentVersion()) > 0) {
|
|
195
|
+
await setUpdateState({ ...currentState, state: 'manual_install_required', operationId: operation.id,
|
|
196
|
+
currentVersion: result.currentVersion || currentVersion(), latestVersion: result.latestVersion,
|
|
197
|
+
managed: true, etag: result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
198
|
+
postponeUntil: LEGACY_GUARD, code: 'update_bootstrap_upgrade_required', message: '',
|
|
199
|
+
manualReason: result.manualInstall?.reason || {}, cancelRequested: false
|
|
200
|
+
}, { operation });
|
|
201
|
+
} else {
|
|
202
|
+
await setUpdateState({ ...currentState, state: 'idle', operationId: operation.id,
|
|
203
|
+
currentVersion: result.currentVersion || currentVersion(),
|
|
204
|
+
latestVersion: result.latestVersion || result.currentVersion || currentVersion(),
|
|
205
|
+
etag: result.etag || currentState.etag || '', lastCheckedAt: checkedAt,
|
|
206
|
+
postponeUntil: LEGACY_GUARD, transactionId: '', code: '', message: '', cancelRequested: false
|
|
207
|
+
}, { operation });
|
|
208
|
+
}
|
|
209
|
+
return getView();
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
212
|
+
await requestNative('update.cancel', { operationId: operation.id }).catch(() => {});
|
|
213
|
+
assertOperation(operation);
|
|
214
|
+
await setUpdateState({ ...currentState, state: 'failed', operationId: operation.id,
|
|
215
|
+
initiatedBy: manual ? 'manual' : 'automatic', postponeUntil: LEGACY_GUARD,
|
|
216
|
+
code: safeCode(error), message: safeMessage(error)
|
|
217
|
+
}, { operation });
|
|
218
|
+
throw error;
|
|
215
219
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
await setUpdateState({
|
|
219
|
-
...currentState,
|
|
220
|
-
state: 'failed',
|
|
221
|
-
initiatedBy: manual ? 'manual' : 'automatic',
|
|
222
|
-
postponeUntil: LEGACY_GUARD,
|
|
223
|
-
code: safeCode(error),
|
|
224
|
-
message: safeMessage(error)
|
|
225
|
-
});
|
|
226
|
-
throw error;
|
|
220
|
+
} finally {
|
|
221
|
+
if (!inheritedOperation) finishOperation(operation);
|
|
227
222
|
}
|
|
228
223
|
}
|
|
229
224
|
|
|
225
|
+
|
|
230
226
|
async function recoverInterruptedCheck() {
|
|
231
227
|
const state = await getUpdateState();
|
|
232
|
-
if (state.state !== 'checking') return;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
state:
|
|
239
|
-
blocker: '',
|
|
240
|
-
blockers: [],
|
|
241
|
-
code: '',
|
|
242
|
-
message: ''
|
|
228
|
+
if (state.state !== 'checking' || activeOperation || state.cancelRequested) return;
|
|
229
|
+
if (state.operationId) {
|
|
230
|
+
await postponeUpdate().catch(() => {});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
await setUpdateState({ ...state,
|
|
234
|
+
state: policy.compareStableVersions(state.latestVersion, currentVersion()) > 0 ? 'update_available' : 'idle',
|
|
235
|
+
blocker: '', blockers: [], code: '', message: ''
|
|
243
236
|
});
|
|
244
237
|
}
|
|
245
238
|
|
|
239
|
+
|
|
246
240
|
async function dismissCompletedUpdate() {
|
|
247
241
|
const state = await getUpdateState();
|
|
248
242
|
if (state.state !== 'committed') return getView();
|
|
@@ -263,194 +257,177 @@
|
|
|
263
257
|
}
|
|
264
258
|
|
|
265
259
|
async function installUpdate() {
|
|
266
|
-
|
|
267
|
-
let consent =
|
|
268
|
-
|
|
269
|
-
if (state.state !== 'update_available' || candidateStale) {
|
|
270
|
-
await checkOnly({ manual: true });
|
|
260
|
+
const operation = beginOperation();
|
|
261
|
+
let state, consent, authorizationId = '';
|
|
262
|
+
try {
|
|
271
263
|
state = await getUpdateState();
|
|
272
264
|
consent = await getConsentState();
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
265
|
+
assertOperation(operation);
|
|
266
|
+
if (state.cancelRequested || state.recoveryPending || policy.isExecutionState(state.state)) return getView();
|
|
267
|
+
const candidateStale = !state.lastCheckedAt || Date.now() - state.lastCheckedAt > CANDIDATE_MAX_AGE_MS;
|
|
268
|
+
if (state.state !== 'update_available' || candidateStale) {
|
|
269
|
+
await checkOnly({ manual: true }, operation);
|
|
270
|
+
assertOperation(operation);
|
|
271
|
+
state = await getUpdateState();
|
|
272
|
+
consent = await getConsentState();
|
|
273
|
+
assertOperation(operation);
|
|
274
|
+
}
|
|
275
|
+
// The fresh check found a release only a reinstall can apply; show that notice.
|
|
276
|
+
if (state.state === 'manual_install_required') return getView();
|
|
277
|
+
if (state.state !== 'update_available'
|
|
278
|
+
|| policy.compareStableVersions(state.latestVersion, currentVersion()) <= 0) {
|
|
279
|
+
throw codedError('update_candidate_missing', 'No newer signed stable update is available.');
|
|
280
|
+
}
|
|
281
|
+
authorizationId = crypto.randomUUID();
|
|
282
|
+
operation.authorizationId = authorizationId;
|
|
283
|
+
operation.targetVersion = state.latestVersion;
|
|
284
|
+
const intent = revocation.prepareAuthorization(consent, authorizationId, state.latestVersion, Date.now());
|
|
285
|
+
await setConsentState(intent, { operation });
|
|
286
|
+
assertOperation(operation);
|
|
288
287
|
await requestNative('update.authorize', {
|
|
289
|
-
authorizationId,
|
|
290
|
-
targetVersion: state.latestVersion,
|
|
291
|
-
currentVersion: currentVersion()
|
|
288
|
+
operationId: operation.id, authorizationId, targetVersion: state.latestVersion, currentVersion: currentVersion()
|
|
292
289
|
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if (!executor || typeof executor.installAuthorizedUpdate !== 'function') {
|
|
304
|
-
throw codedError(
|
|
305
|
-
'update_executor_unavailable',
|
|
306
|
-
'The managed update executor is unavailable.'
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
await executor.installAuthorizedUpdate();
|
|
290
|
+
assertOperation(operation);
|
|
291
|
+
await setConsentState(revocation.clear({ ...intent, snoozedVersion: '', snoozedUntil: 0,
|
|
292
|
+
authorizedVersion: state.latestVersion, authorizationId, authorizedAt: Date.now()
|
|
293
|
+
}), { operation });
|
|
294
|
+
await setUpdateState({ ...state, operationId: operation.id, postponeUntil: LEGACY_GUARD,
|
|
295
|
+
cancelRequested: false, recoveryPending: false, code: '', message: ''
|
|
296
|
+
}, { operation });
|
|
297
|
+
// Replaceable runtime owns this transfer. The immutable Bootstrap keeps
|
|
298
|
+
// its health-confirmation role and cannot resume a postponed operation.
|
|
299
|
+
await stageAuthorizedUpdate(operation);
|
|
310
300
|
return getView();
|
|
311
301
|
} catch (error) {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
} catch (revokeError) {
|
|
333
|
-
if (error && typeof error === 'object') {
|
|
302
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
303
|
+
if (authorizationId) {
|
|
304
|
+
const observed = await getConsentState();
|
|
305
|
+
assertOperation(operation);
|
|
306
|
+
const pending = revocation.prepareAuthorization({ ...observed,
|
|
307
|
+
authorizedVersion: state.latestVersion, authorizationId,
|
|
308
|
+
authorizedAt: observed.authorizedAt || Date.now()
|
|
309
|
+
}, authorizationId, state.latestVersion, Date.now());
|
|
310
|
+
await setConsentState(pending, { operation });
|
|
311
|
+
try {
|
|
312
|
+
await requestNative('update.revoke', {
|
|
313
|
+
operationId: operation.id, authorizationId, targetVersion: state.latestVersion
|
|
314
|
+
});
|
|
315
|
+
assertOperation(operation);
|
|
316
|
+
await setConsentState(revocation.clear({ ...pending,
|
|
317
|
+
authorizedVersion: '', authorizationId: '', authorizedAt: 0
|
|
318
|
+
}), { operation });
|
|
319
|
+
} catch (revokeError) {
|
|
320
|
+
if (!isCurrentOperation(operation)) return getView();
|
|
321
|
+
operation.revocationPending = true;
|
|
334
322
|
error.revocationError = safeMessage(revokeError);
|
|
335
323
|
}
|
|
336
324
|
}
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
message: safeMessage(error)
|
|
347
|
-
});
|
|
325
|
+
const observedState = await getUpdateState();
|
|
326
|
+
assertOperation(operation);
|
|
327
|
+
if (!['awaiting_health', 'committed', 'rolled_back'].includes(observedState.state)) {
|
|
328
|
+
await setUpdateState({ ...observedState, state: 'failed', initiatedBy: 'manual',
|
|
329
|
+
blocker: '', blockers: [], code: safeCode(error), message: safeMessage(error),
|
|
330
|
+
operationId: operation.id,
|
|
331
|
+
cancelRequested: operation.phase !== 'applying' && operation.revocationPending === true,
|
|
332
|
+
recoveryPending: operation.phase === 'applying'
|
|
333
|
+
}, { operation });
|
|
348
334
|
}
|
|
349
|
-
await armLegacyGuard();
|
|
350
335
|
throw error;
|
|
351
|
-
}
|
|
336
|
+
} finally { finishOperation(operation); }
|
|
352
337
|
}
|
|
353
338
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
initiatedBy: 'manual',
|
|
370
|
-
latestVersion: staged.targetVersion,
|
|
371
|
-
transactionId: staged.transactionId,
|
|
372
|
-
stagedAt: Date.now(),
|
|
373
|
-
blocker: '',
|
|
374
|
-
blockers: [],
|
|
375
|
-
code: '',
|
|
376
|
-
message: ''
|
|
377
|
-
});
|
|
378
|
-
return tryActivateStagedUpdate();
|
|
339
|
+
|
|
340
|
+
async function stageAuthorizedUpdate(operation) {
|
|
341
|
+
operation.phase = 'downloading';
|
|
342
|
+
await setUpdateState({ state: 'downloading', operationId: operation.id,
|
|
343
|
+
initiatedBy: 'manual', blocker: '', blockers: [], code: '', message: '',
|
|
344
|
+
postponeUntil: LEGACY_GUARD
|
|
345
|
+
}, { operation });
|
|
346
|
+
assertOperation(operation);
|
|
347
|
+
const staged = await requestNative('update.stage', { operationId: operation.id });
|
|
348
|
+
assertOperation(operation);
|
|
349
|
+
await setUpdateState({ state: 'staged', operationId: operation.id, initiatedBy: 'manual',
|
|
350
|
+
latestVersion: staged.targetVersion, transactionId: staged.transactionId, stagedAt: Date.now(),
|
|
351
|
+
blocker: '', blockers: [], code: '', message: '', postponeUntil: LEGACY_GUARD
|
|
352
|
+
}, { operation });
|
|
353
|
+
return tryActivateStagedUpdate(operation);
|
|
379
354
|
}
|
|
380
355
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (typeof executor?.installAuthorizedUpdate === 'function') {
|
|
384
|
-
if (activationPromise) return activationPromise;
|
|
385
|
-
activationPromise = Promise.resolve(executor.installAuthorizedUpdate()).finally(() => {
|
|
386
|
-
activationPromise = null;
|
|
387
|
-
});
|
|
388
|
-
return activationPromise;
|
|
389
|
-
}
|
|
356
|
+
|
|
357
|
+
async function tryActivateStagedUpdate(inheritedOperation) {
|
|
390
358
|
if (activationPromise) return activationPromise;
|
|
391
|
-
|
|
359
|
+
const state = await getUpdateState();
|
|
360
|
+
if (state.cancelRequested || state.recoveryPending || cancellationPromise
|
|
361
|
+
|| !['staged', 'waiting_for_idle'].includes(state.state)) return state;
|
|
362
|
+
const operation = inheritedOperation || beginOperation(state.operationId);
|
|
363
|
+
assertOperation(operation);
|
|
364
|
+
activationPromise = tryActivateStagedUpdateCore(operation).finally(() => {
|
|
392
365
|
activationPromise = null;
|
|
366
|
+
if (!inheritedOperation) finishOperation(operation);
|
|
393
367
|
});
|
|
394
368
|
return activationPromise;
|
|
395
369
|
}
|
|
396
370
|
|
|
397
|
-
|
|
371
|
+
|
|
372
|
+
async function tryActivateStagedUpdateCore(operation) {
|
|
398
373
|
clearIdleRetryTimer();
|
|
399
374
|
const state = await getUpdateState();
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
const surfaceTabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES }).catch(() => []);
|
|
375
|
+
assertOperation(operation);
|
|
376
|
+
if (state.cancelRequested || !['staged', 'waiting_for_idle'].includes(state.state)) return state;
|
|
377
|
+
operation.phase = 'waiting_for_idle';
|
|
378
|
+
const surfaceTabs = await chrome.tabs.query({ url: OVERLEAF_MATCHES });
|
|
405
379
|
const editorTabs = surfaceTabs.filter(isUsableEditorTab);
|
|
406
|
-
const reloadTabs = surfaceTabs
|
|
407
|
-
.filter(tab => Number.isInteger(tab?.id) && !tab.discarded && tab.status !== 'unloaded')
|
|
408
|
-
.map(tab => tab.id);
|
|
380
|
+
const reloadTabs = surfaceTabs.filter(tab => Number.isInteger(tab?.id) && !tab.discarded && tab.status !== 'unloaded').map(tab => tab.id);
|
|
409
381
|
const probes = await Promise.all(editorTabs.map(tab => probeTabIdle(tab.id)));
|
|
410
382
|
const nativeGate = await requestNative('update.canApply')
|
|
411
|
-
.then(result => ({ ok: true, result }))
|
|
412
|
-
|
|
383
|
+
.then(result => ({ ok: true, result })).catch(error => ({ ok: false, error: safeError(error) }));
|
|
384
|
+
assertOperation(operation);
|
|
413
385
|
const blockers = root.CodexOverleafUpdateStatus?.collectBlockers(probes, nativeGate) || ['busy'];
|
|
386
|
+
if (nativeBridge?.getPendingState?.().executionRequests > 0) blockers.push('background_execution_pending');
|
|
414
387
|
if (blockers.length) {
|
|
415
|
-
const waiting = await setUpdateState({
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
blockers
|
|
420
|
-
});
|
|
421
|
-
scheduleIdleRetry(blockers);
|
|
388
|
+
const waiting = await setUpdateState({ state: 'waiting_for_idle',
|
|
389
|
+
operationId: operation.id, blocker: blockers[0], blockers
|
|
390
|
+
}, { operation });
|
|
391
|
+
if (isCurrentOperation(operation)) scheduleIdleRetry(blockers);
|
|
422
392
|
return waiting;
|
|
423
393
|
}
|
|
424
|
-
|
|
394
|
+
operation.phase = 'applying';
|
|
425
395
|
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
396
|
+
assertOperation(operation);
|
|
426
397
|
await chrome.storage.local.set({ [UPDATE_RELOAD_TABS_KEY]: reloadTabs });
|
|
427
|
-
await setUpdateState({
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
398
|
+
await setUpdateState({ state: 'applying', operationId: operation.id,
|
|
399
|
+
blocker: '', blockers: [], code: '', message: ''
|
|
400
|
+
}, { operation });
|
|
401
|
+
assertOperation(operation);
|
|
402
|
+
// requestInternal rechecks the safe point and blocks new native work.
|
|
403
|
+
let applied;
|
|
404
|
+
try {
|
|
405
|
+
applied = await requestNative('update.apply', {
|
|
406
|
+
operationId: operation.id, transactionId: state.transactionId
|
|
407
|
+
});
|
|
408
|
+
} catch (error) {
|
|
409
|
+
assertOperation(operation);
|
|
410
|
+
if (!['update_not_idle', 'update_native_busy'].includes(error.code)) throw error;
|
|
411
|
+
operation.phase = 'waiting_for_idle';
|
|
412
|
+
const waiting = await setUpdateState({ state: 'waiting_for_idle',
|
|
413
|
+
blocker: 'busy', blockers: ['busy'], recoveryPending: false
|
|
414
|
+
}, { operation, observed: true });
|
|
415
|
+
scheduleIdleRetry(waiting.blockers);
|
|
416
|
+
return waiting;
|
|
441
417
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
message: ''
|
|
449
|
-
});
|
|
418
|
+
assertOperation(operation);
|
|
419
|
+
if (applied.state !== 'awaiting_health' || applied.transactionId !== state.transactionId) {
|
|
420
|
+
throw codedError('update_apply_unconfirmed', 'Update activation did not return matching transaction evidence.');
|
|
421
|
+
}
|
|
422
|
+
await setUpdateState({ state: 'awaiting_health', operationId: operation.id,
|
|
423
|
+
latestVersion: applied.targetVersion, transactionId: applied.transactionId,
|
|
424
|
+
recoveryPending: false, code: '', message: ''
|
|
425
|
+
}, { operation });
|
|
450
426
|
chrome.runtime.reload();
|
|
451
427
|
return { state: 'awaiting_health' };
|
|
452
428
|
}
|
|
453
429
|
|
|
430
|
+
|
|
454
431
|
async function waitForActivatedTransaction(transactionId) {
|
|
455
432
|
const deadline = Date.now() + ACTIVATION_TIMEOUT_MS;
|
|
456
433
|
while (Date.now() <= deadline) {
|
|
@@ -486,7 +463,7 @@
|
|
|
486
463
|
try {
|
|
487
464
|
const url = new URL(tab.url || '');
|
|
488
465
|
return url.protocol === 'https:' &&
|
|
489
|
-
(
|
|
466
|
+
(['overleaf.com', 'www.overleaf.com', 'cn.overleaf.com'].includes(url.hostname)) &&
|
|
490
467
|
/^\/project\/[^/]+(?:\/|$)/.test(url.pathname);
|
|
491
468
|
} catch (_error) {
|
|
492
469
|
return false;
|
|
@@ -505,142 +482,177 @@
|
|
|
505
482
|
}
|
|
506
483
|
}
|
|
507
484
|
|
|
508
|
-
|
|
485
|
+
// A reinstall notice has no download or authorization to cancel; Later only
|
|
486
|
+
// hides it for a day.
|
|
487
|
+
async function snoozeManualInstall() {
|
|
509
488
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
510
|
-
if (
|
|
511
|
-
|
|
512
|
-
}
|
|
513
|
-
let pendingConsent = consent;
|
|
514
|
-
if (consent.authorizationId) {
|
|
515
|
-
pendingConsent = revocation.begin(consent, state, Date.now());
|
|
516
|
-
await setConsentState(pendingConsent);
|
|
517
|
-
try {
|
|
518
|
-
await requestNative('update.revoke', {
|
|
519
|
-
authorizationId: pendingConsent.revokingAuthorizationId,
|
|
520
|
-
targetVersion: pendingConsent.revokingVersion,
|
|
521
|
-
transactionId: pendingConsent.revokingTransactionId
|
|
522
|
-
});
|
|
523
|
-
} catch (error) {
|
|
524
|
-
if (error?.code === 'update_revoke_too_late') {
|
|
525
|
-
await setConsentState(revocation.clear(pendingConsent));
|
|
526
|
-
return getView();
|
|
527
|
-
}
|
|
528
|
-
throw error;
|
|
529
|
-
}
|
|
530
|
-
} else if (['staged', 'waiting_for_idle'].includes(state.state)) {
|
|
531
|
-
throw codedError('update_consent_mismatch', 'The staged update has no matching runtime authorization.');
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
clearIdleRetryTimer();
|
|
535
|
-
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
536
|
-
const completed = revocation.complete(state, pendingConsent, {
|
|
537
|
-
now: Date.now(),
|
|
538
|
-
snoozeMs: SNOOZE_MS,
|
|
539
|
-
postponeUntil: LEGACY_GUARD
|
|
540
|
-
});
|
|
541
|
-
await setUpdateAndConsentState(completed.updateState, completed.consentState);
|
|
489
|
+
if (state.state !== 'manual_install_required') return null;
|
|
490
|
+
await setConsentState({ ...consent, snoozedVersion: state.latestVersion, snoozedUntil: Date.now() + SNOOZE_MS });
|
|
542
491
|
return getView();
|
|
543
492
|
}
|
|
544
493
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
494
|
+
function postponeUpdate() {
|
|
495
|
+
if (cancellationPromise) return cancellationPromise;
|
|
496
|
+
cancellationPromise = cancelUpdateCore().finally(() => { cancellationPromise = null; });
|
|
497
|
+
return cancellationPromise;
|
|
548
498
|
}
|
|
549
499
|
|
|
550
|
-
async function
|
|
500
|
+
async function cancelUpdateCore() {
|
|
551
501
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
552
|
-
if (
|
|
553
|
-
|
|
502
|
+
if (['applying', 'awaiting_health', 'rolling_back'].includes(state.state)
|
|
503
|
+
|| activeOperation?.phase === 'applying' || state.recoveryPending) {
|
|
504
|
+
throw codedError('update_revoke_too_late', 'The update is being installed or recovered.');
|
|
505
|
+
}
|
|
506
|
+
const operation = activeOperation;
|
|
507
|
+
const generation = ++operationGeneration;
|
|
508
|
+
activeOperation = null;
|
|
509
|
+
clearIdleRetryTimer();
|
|
510
|
+
const operationId = operation?.id || state.operationId || '';
|
|
511
|
+
const authorizationId = consent.authorizationId || consent.revokingAuthorizationId || operation?.authorizationId || '';
|
|
512
|
+
const targetVersion = consent.authorizedVersion || consent.revokingVersion || operation?.targetVersion || state.latestVersion;
|
|
513
|
+
const pending = authorizationId ? revocation.begin({ ...consent, authorizationId,
|
|
514
|
+
authorizedVersion: targetVersion }, state, Date.now()) : consent;
|
|
515
|
+
await setUpdateAndConsentState({ ...state, operationId, cancelRequested: true,
|
|
516
|
+
postponeUntil: LEGACY_GUARD, code: '', message: ''
|
|
517
|
+
}, pending, { generation });
|
|
518
|
+
await chrome.alarms?.clear?.(IDLE_ALARM).catch(() => {});
|
|
554
519
|
try {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
520
|
+
if (operationId) {
|
|
521
|
+
const result = await withTimeout(requestNative('update.cancel', { operationId }), 15000,
|
|
522
|
+
codedError('update_cancel_unconfirmed', 'Update cancellation has not been acknowledged yet.'));
|
|
523
|
+
if (result.state !== 'cancelled' || result.operationId !== operationId) {
|
|
524
|
+
throw codedError('update_cancel_unconfirmed', 'Update cancellation returned incomplete evidence.');
|
|
525
|
+
}
|
|
526
|
+
} else if (state.state === 'downloading') {
|
|
527
|
+
throw codedError('update_cancel_unconfirmed', 'This older download has no operation identity. Reload the extension to recover it.');
|
|
528
|
+
}
|
|
529
|
+
if (authorizationId) {
|
|
530
|
+
await withTimeout(requestNative('update.revoke', {
|
|
531
|
+
operationId, authorizationId, targetVersion
|
|
532
|
+
}), 15000, codedError('update_cancel_unconfirmed', 'Update authorization cleanup has not finished yet.'));
|
|
564
533
|
}
|
|
565
|
-
|
|
566
|
-
|
|
534
|
+
const completed = revocation.complete(state, pending, { now: Date.now(),
|
|
535
|
+
snoozeMs: SNOOZE_MS, postponeUntil: LEGACY_GUARD });
|
|
536
|
+
await setUpdateAndConsentState({ ...completed.updateState,
|
|
537
|
+
state: policy.compareStableVersions(state.latestVersion, currentVersion()) > 0 ? 'update_available' : 'idle',
|
|
538
|
+
operationId: '', cancelRequested: false, recoveryPending: false,
|
|
539
|
+
code: 'update_cancelled', message: ''
|
|
540
|
+
}, completed.consentState, { generation, observed: true });
|
|
541
|
+
return getView();
|
|
542
|
+
} catch (error) {
|
|
543
|
+
if (error.code === 'update_revoke_too_late') {
|
|
544
|
+
await setUpdateAndConsentState({ cancelRequested: false, recoveryPending: true,
|
|
545
|
+
state: 'failed', code: 'update_recovery_pending', message: 'The update reached installation before cancellation. Checking its result.'
|
|
546
|
+
}, revocation.clear(pending), { generation, observed: true });
|
|
547
|
+
} else {
|
|
548
|
+
await setUpdateState({ cancelRequested: true, code: 'update_cancel_unconfirmed',
|
|
549
|
+
message: safeMessage(error)
|
|
550
|
+
}, { generation });
|
|
567
551
|
}
|
|
552
|
+
throw error;
|
|
568
553
|
}
|
|
554
|
+
}
|
|
569
555
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
556
|
+
|
|
557
|
+
function reconcileRecoveryState() {
|
|
558
|
+
if (recoveryPromise) return recoveryPromise;
|
|
559
|
+
recoveryPromise = (async () => {
|
|
560
|
+
const state = await getUpdateState();
|
|
561
|
+
if (state.recoveryPending) return reconcileExpiredPhase();
|
|
562
|
+
if (state.cancelRequested) return postponeUpdate();
|
|
563
|
+
if (!activeOperation) await reconcilePendingRevocation();
|
|
564
|
+
return reconcileExpiredPhase();
|
|
565
|
+
})().finally(() => { recoveryPromise = null; });
|
|
566
|
+
return recoveryPromise;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
async function reconcilePendingRevocation() {
|
|
571
|
+
const consent = await getConsentState();
|
|
572
|
+
if (!revocation.hasPending(consent) || activeOperation) return getUpdateState();
|
|
573
|
+
return postponeUpdate().then(view => view.state);
|
|
582
574
|
}
|
|
583
575
|
|
|
576
|
+
|
|
584
577
|
async function settleTerminalConsent() {
|
|
585
578
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
586
|
-
if (!policy.isTerminalState(state.state) ||
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
authorizationId: '',
|
|
591
|
-
authorizedAt: 0
|
|
592
|
-
});
|
|
579
|
+
if (!policy.isTerminalState(state.state) || state.cancelRequested || state.recoveryPending
|
|
580
|
+
|| revocation.hasPending(consent) || !consent.authorizationId) return;
|
|
581
|
+
await setConsentState({ ...consent, authorizedVersion: '', authorizationId: '', authorizedAt: 0 },
|
|
582
|
+
{ expectedAuthorizationId: consent.authorizationId });
|
|
593
583
|
}
|
|
594
584
|
|
|
585
|
+
|
|
595
586
|
async function reconcileExpiredPhase() {
|
|
596
587
|
const state = await getUpdateState();
|
|
597
|
-
if (!state.deadlineAt || Date.now() <= state.deadlineAt
|
|
598
|
-
return state;
|
|
588
|
+
if (!state.recoveryPending && (!state.deadlineAt || Date.now() <= state.deadlineAt
|
|
589
|
+
|| policy.isTerminalState(state.state))) return state;
|
|
590
|
+
if (['checking', 'downloading'].includes(state.state)) {
|
|
591
|
+
await postponeUpdate();
|
|
592
|
+
return setUpdateState({ state: 'failed', cancelRequested: false,
|
|
593
|
+
code: 'update_phase_timeout', message: 'The update timed out and its download was stopped. Retry when the connection is available.'
|
|
594
|
+
}, { observed: true });
|
|
599
595
|
}
|
|
600
|
-
if (['applying', 'awaiting_health', 'rolling_back'].includes(state.state)) {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
state
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
596
|
+
if (state.recoveryPending || ['applying', 'awaiting_health', 'rolling_back'].includes(state.state)) {
|
|
597
|
+
try {
|
|
598
|
+
let status = await withTimeout(requestNative('update.status'), 10000,
|
|
599
|
+
codedError('update_status_timeout', 'Update recovery status is unavailable.'));
|
|
600
|
+
let transaction = status.transaction;
|
|
601
|
+
if (state.transactionId && transaction?.id !== state.transactionId) {
|
|
602
|
+
throw codedError('update_transaction_mismatch', 'The update recovery transaction could not be matched.');
|
|
603
|
+
}
|
|
604
|
+
if (transaction?.state === 'staged' || (!transaction && status.installedAligned
|
|
605
|
+
&& status.activeVersion === state.currentVersion)) {
|
|
606
|
+
if (state.operationId) await requestNative('update.cancel', { operationId: state.operationId });
|
|
607
|
+
const consent = await getConsentState();
|
|
608
|
+
const authorizationId = consent.authorizationId || consent.revokingAuthorizationId;
|
|
609
|
+
const targetVersion = consent.authorizedVersion || consent.revokingVersion || state.latestVersion;
|
|
610
|
+
if (authorizationId) await requestNative('update.revoke', { authorizationId, targetVersion });
|
|
611
|
+
await setConsentState(revocation.clear({ ...consent,
|
|
612
|
+
authorizedVersion: '', authorizationId: '', authorizedAt: 0
|
|
613
|
+
}));
|
|
614
|
+
return setUpdateState({ state: 'failed', recoveryPending: false, cancelRequested: false,
|
|
615
|
+
operationId: '', code: 'update_not_applied', message: 'The update was stopped before installation. Retry when ready.'
|
|
616
|
+
}, { observed: true });
|
|
617
|
+
}
|
|
618
|
+
if (transaction?.state === 'awaiting_health') {
|
|
619
|
+
const rolledBack = await requestNative('update.rollback', {
|
|
620
|
+
transactionId: transaction.id, expectedState: 'awaiting_health', reasonCode: 'update_health_timeout'
|
|
621
|
+
});
|
|
622
|
+
if (rolledBack.state !== 'rolled_back' || rolledBack.version !== transaction.sourceVersion) {
|
|
623
|
+
throw codedError('update_rollback_unconfirmed', 'Rollback returned incomplete evidence.');
|
|
624
|
+
}
|
|
625
|
+
status = await requestNative('update.status');
|
|
626
|
+
transaction = status.transaction;
|
|
627
|
+
}
|
|
628
|
+
if (transaction?.state === 'committed' && status.installedAligned
|
|
629
|
+
&& status.activeVersion === transaction.targetVersion) {
|
|
630
|
+
return setUpdateState({ state: 'committed', currentVersion: transaction.targetVersion,
|
|
631
|
+
latestVersion: transaction.targetVersion, transactionId: transaction.id,
|
|
632
|
+
recoveryPending: false, cancelRequested: false, code: '', message: ''
|
|
633
|
+
}, { observed: true });
|
|
634
|
+
}
|
|
635
|
+
if (transaction?.state === 'rolled_back' && status.installedAligned
|
|
636
|
+
&& status.activeVersion === transaction.sourceVersion
|
|
637
|
+
&& (!state.transactionId || transaction.id === state.transactionId)) {
|
|
638
|
+
const restored = await setUpdateState({ state: 'rolled_back', currentVersion: transaction.sourceVersion,
|
|
639
|
+
transactionId: transaction.id, recoveryPending: false, cancelRequested: false,
|
|
640
|
+
code: transaction.reasonCode || 'update_rolled_back', message: 'The previous version has been restored.'
|
|
641
|
+
}, { observed: true });
|
|
642
|
+
setTimeout(() => chrome.runtime.reload(), 0);
|
|
643
|
+
return restored;
|
|
644
|
+
}
|
|
645
|
+
throw codedError('update_recovery_pending', 'The installed update result has not been confirmed.');
|
|
646
|
+
} catch (error) {
|
|
647
|
+
return setUpdateState({ state: 'failed', recoveryPending: true, cancelRequested: false,
|
|
648
|
+
code: safeCode(error), message: safeMessage(error)
|
|
633
649
|
}, { observed: true });
|
|
634
650
|
}
|
|
635
651
|
}
|
|
636
|
-
return
|
|
637
|
-
...state,
|
|
638
|
-
state: 'failed',
|
|
639
|
-
code: 'update_phase_timeout',
|
|
640
|
-
message: 'The managed update phase did not complete in time. Retry, or use the manual update command.'
|
|
641
|
-
});
|
|
652
|
+
return state;
|
|
642
653
|
}
|
|
643
654
|
|
|
655
|
+
|
|
644
656
|
async function handleObservedStateChange() {
|
|
645
657
|
await settleTerminalConsent();
|
|
646
658
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
@@ -653,14 +665,14 @@
|
|
|
653
665
|
async function armLegacyGuard() {
|
|
654
666
|
const [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
655
667
|
if (consent.authorizationId || state.postponeUntil === LEGACY_GUARD) return state;
|
|
656
|
-
return setUpdateState({
|
|
668
|
+
return setUpdateState({ postponeUntil: LEGACY_GUARD });
|
|
657
669
|
}
|
|
658
670
|
|
|
659
671
|
async function getView() {
|
|
660
672
|
let [state, consent] = await Promise.all([getUpdateState(), getConsentState()]);
|
|
661
|
-
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId;
|
|
673
|
+
const busy = state.state === 'checking' || policy.isExecutionState(state.state) || consent.authorizationId || state.cancelRequested;
|
|
662
674
|
const runtime = busy ? { state: 'transaction_active' } : await readRuntimeStatus();
|
|
663
|
-
if (runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
675
|
+
if (!state.recoveryPending && !state.cancelRequested && runtime.state === 'aligned' && ['failed', 'rolled_back'].includes(state.state) &&
|
|
664
676
|
policy.compareStableVersions(state.latestVersion || state.currentVersion, runtime.installedVersion) <= 0) {
|
|
665
677
|
state = await setUpdateState({ ...state, state: 'idle', currentVersion: runtime.installedVersion,
|
|
666
678
|
latestVersion: runtime.installedVersion, code: '', message: '', transactionId: '', blocker: '', blockers: [] });
|
|
@@ -769,17 +781,23 @@
|
|
|
769
781
|
return policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
770
782
|
}
|
|
771
783
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
784
|
+
function setUpdateState(value, options = {}) {
|
|
785
|
+
return enqueueStateWrite(async () => {
|
|
786
|
+
assertWriteOwner(options);
|
|
787
|
+
const stored = await chrome.storage.local.get(UPDATE_STATE_KEY);
|
|
788
|
+
assertWriteOwner(options);
|
|
789
|
+
const previous = policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
790
|
+
const next = projectUpdateState(previous, value, options);
|
|
791
|
+
await chrome.storage.local.set({ [UPDATE_STATE_KEY]: next });
|
|
792
|
+
return next;
|
|
793
|
+
});
|
|
778
794
|
}
|
|
779
795
|
|
|
796
|
+
|
|
780
797
|
function projectUpdateState(previous, value, options = {}) {
|
|
781
798
|
const now = Date.now();
|
|
782
799
|
const candidate = {
|
|
800
|
+
...previous,
|
|
783
801
|
...value,
|
|
784
802
|
initiatedBy: value.initiatedBy || previous.initiatedBy
|
|
785
803
|
};
|
|
@@ -805,24 +823,35 @@
|
|
|
805
823
|
return policy.normalizeConsentState(stored?.[CONSENT_STATE_KEY]);
|
|
806
824
|
}
|
|
807
825
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
826
|
+
function setConsentState(value, options = {}) {
|
|
827
|
+
return enqueueStateWrite(async () => {
|
|
828
|
+
assertWriteOwner(options);
|
|
829
|
+
if (options.expectedAuthorizationId) {
|
|
830
|
+
const current = await getConsentState();
|
|
831
|
+
if (current.authorizationId !== options.expectedAuthorizationId) return current;
|
|
832
|
+
}
|
|
833
|
+
assertWriteOwner(options);
|
|
834
|
+
const next = policy.normalizeConsentState(value);
|
|
835
|
+
await chrome.storage.local.set({ [CONSENT_STATE_KEY]: next });
|
|
836
|
+
return next;
|
|
837
|
+
});
|
|
812
838
|
}
|
|
813
839
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
[UPDATE_STATE_KEY]
|
|
821
|
-
|
|
840
|
+
|
|
841
|
+
function setUpdateAndConsentState(updateValue, consentValue, options = {}) {
|
|
842
|
+
return enqueueStateWrite(async () => {
|
|
843
|
+
assertWriteOwner(options);
|
|
844
|
+
const stored = await chrome.storage.local.get([UPDATE_STATE_KEY, CONSENT_STATE_KEY]);
|
|
845
|
+
assertWriteOwner(options);
|
|
846
|
+
const previous = policy.normalizeUpdateState(stored?.[UPDATE_STATE_KEY], currentVersion());
|
|
847
|
+
const updateState = projectUpdateState(previous, updateValue, options);
|
|
848
|
+
const consentState = policy.normalizeConsentState(consentValue);
|
|
849
|
+
await chrome.storage.local.set({ [UPDATE_STATE_KEY]: updateState, [CONSENT_STATE_KEY]: consentState });
|
|
850
|
+
return { updateState, consentState };
|
|
822
851
|
});
|
|
823
|
-
return { updateState, consentState };
|
|
824
852
|
}
|
|
825
853
|
|
|
854
|
+
|
|
826
855
|
async function requestNative(method, params = {}) {
|
|
827
856
|
const response = await nativeBridge?.requestInternal?.({
|
|
828
857
|
id: crypto.randomUUID(),
|
|
@@ -839,11 +868,45 @@
|
|
|
839
868
|
}
|
|
840
869
|
|
|
841
870
|
function enqueuePolicyAction(action) {
|
|
842
|
-
const
|
|
871
|
+
const generation = operationGeneration;
|
|
872
|
+
const guarded = () => generation !== operationGeneration || cancellationPromise ? getView() : action();
|
|
873
|
+
const result = policyTail.then(guarded, guarded);
|
|
843
874
|
policyTail = result.catch(() => {});
|
|
844
875
|
return result;
|
|
845
876
|
}
|
|
846
877
|
|
|
878
|
+
function beginOperation(id) {
|
|
879
|
+
const operation = { id: id || crypto.randomUUID(), generation: ++operationGeneration, phase: 'checking' };
|
|
880
|
+
activeOperation = operation;
|
|
881
|
+
return operation;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function isCurrentOperation(operation) {
|
|
885
|
+
return activeOperation === operation && operation.generation === operationGeneration;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function assertOperation(operation) {
|
|
889
|
+
if (!isCurrentOperation(operation)) throw codedError('update_network_cancelled', 'The update request was cancelled.');
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function finishOperation(operation) {
|
|
893
|
+
if (activeOperation === operation) activeOperation = null;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function assertWriteOwner(options) {
|
|
897
|
+
if (options.operation) assertOperation(options.operation);
|
|
898
|
+
if (options.generation !== undefined && options.generation !== operationGeneration) {
|
|
899
|
+
throw codedError('update_network_cancelled', 'The update request was superseded.');
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
function enqueueStateWrite(action) {
|
|
904
|
+
const result = stateWriteTail.then(action, action);
|
|
905
|
+
stateWriteTail = result.catch(() => {});
|
|
906
|
+
return result;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
|
|
847
910
|
function isAllowedSender(sender) {
|
|
848
911
|
if (sender?.id !== chrome.runtime.id) return false;
|
|
849
912
|
try {
|
|
@@ -853,7 +916,7 @@
|
|
|
853
916
|
return url.pathname === '/bootstrap/popup.html';
|
|
854
917
|
}
|
|
855
918
|
return url.protocol === 'https:' &&
|
|
856
|
-
(
|
|
919
|
+
(['overleaf.com', 'www.overleaf.com', 'cn.overleaf.com'].includes(url.hostname)) &&
|
|
857
920
|
(url.pathname === '/project' || url.pathname.startsWith('/project/'));
|
|
858
921
|
} catch (_error) {
|
|
859
922
|
return false;
|