codex-overleaf-link 2.4.1 → 2.5.1
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 +275 -305
- package/README.zh-CN.md +290 -320
- package/extension/bootstrap/background.js +99 -19
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +11 -16
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/update.js +25 -2
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +234 -56
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +440 -0
- package/extension/src/backgroundUpdateCoordinator.js +541 -381
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +16 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +244 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +117 -67
- package/extension/src/content/composerPanel.js +41 -8
- package/extension/src/content/contentRuntime.js +1084 -1099
- package/extension/src/content/diagnosticsController.js +122 -97
- package/extension/src/content/diagnosticsExport.js +236 -0
- package/extension/src/content/diagnosticsPanel.js +78 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +14680 -3606
- package/extension/src/content/generated/content.bundle.meta.json +30 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +25 -1
- package/extension/src/content/localSkillsPanel.js +141 -16
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +8 -5
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +33 -5
- package/extension/src/content/panelMaintenance.js +87 -132
- package/extension/src/content/panelRenderer.js +10 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +63 -14
- package/extension/src/content/providerSettingsDialog.js +724 -113
- package/extension/src/content/recentProjects.js +21 -6
- package/extension/src/content/runActivitySummary.js +665 -0
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +40 -15
- package/extension/src/content/runFailureNotice.js +34 -12
- package/extension/src/content/runGuidanceController.js +76 -30
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +150 -43
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +11 -8
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +315 -290
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/sessionPersistence.js +6 -1
- package/extension/src/content/settingsPanel.js +84 -55
- package/extension/src/content/settingsWorkbench.js +379 -0
- package/extension/src/content/sharedSessionViewSync.js +109 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +179 -56
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +308 -2
- package/extension/src/content/writebackOrchestrator.js +213 -199
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +600 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +223 -23
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +149 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +20 -6
- package/extension/src/page/snapshotRouter.js +124 -48
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +442 -71
- package/extension/src/page/trackedChangeCapture.js +53 -57
- package/extension/src/page/trackedChangeOwnership.js +216 -0
- package/extension/src/page/trackedChangeReplay.js +184 -0
- package/extension/src/page/trackedChangesLifecycle.js +343 -339
- package/extension/src/page/treeOperations.js +33 -12
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +114 -0
- package/extension/src/page/writebackRouter.js +242 -201
- package/extension/src/pageBridge.js +73 -102
- package/extension/src/popup.js +261 -65
- package/extension/src/shared/agentTranscript.js +41 -4
- package/extension/src/shared/auditRecords.js +73 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +101 -37
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +329 -3
- package/extension/src/shared/globalPreferences.js +31 -14
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +389 -25
- package/extension/src/shared/managedUpdateProjection.js +8 -5
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +9 -2
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +114 -3
- 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/runInputQueue.js +48 -5
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +37 -0
- package/extension/src/shared/sessionState.js +166 -30
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +311 -0
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +77 -78
- package/extension/src/shared/storageMigration.js +74 -111
- package/extension/src/shared/storageRunActions.js +211 -22
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +216 -6
- package/extension/src/shared/updateConsent.js +42 -11
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +78 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +67 -48
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +1987 -385
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +6 -3
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +33 -12
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +211 -318
- package/native-host/src/codexSessionTiming.js +21 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +115 -39
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +115 -0
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +17 -3
- package/native-host/src/providerReasoning.js +10 -11
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +76 -14
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +86 -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/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +176 -159
- package/native-host/src/subagentTelemetry.js +325 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +34 -0
- package/native-host/src/taskRunnerRuntime.js +130 -57
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +406 -38
- 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 +475 -0
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- package/scripts/install-managed.mjs +1 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) module.exports = factory(require('./sharedSessionTransfer'));
|
|
3
|
+
else root.CodexOverleafModuleRegistry.define('SharedSessionBridge', ['SharedSessionTransfer'], factory);
|
|
4
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (Transfer) {
|
|
5
|
+
'use strict';
|
|
6
|
+
const MESSAGE = 'codex-overleaf/shared-sessions-v1';
|
|
7
|
+
const LOCK_PORT = 'codex-overleaf/shared-session-lock-v1';
|
|
8
|
+
const CHANGE_KEY = 'codex-overleaf-shared-sessions-changed-v1';
|
|
9
|
+
const BASE = Symbol('shared-session-base');
|
|
10
|
+
const failure = (code, message) => Object.assign(new Error(message), { code });
|
|
11
|
+
const clone = value => value == null ? value : JSON.parse(JSON.stringify(value));
|
|
12
|
+
const clean = record => {
|
|
13
|
+
const result = clone(record);
|
|
14
|
+
if (result) delete result.sharedRevision;
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function createBridge(environment = globalThis) {
|
|
19
|
+
const chromeApi = environment.chrome;
|
|
20
|
+
const clientId = environment.crypto?.randomUUID?.() || 'page-' + Date.now() + '-' + Math.random();
|
|
21
|
+
const migrations = new Map(), baselines = new Map(), heldLocks = new Map();
|
|
22
|
+
let legacy, wrapped;
|
|
23
|
+
function enabled() {
|
|
24
|
+
return Boolean(chromeApi?.runtime?.sendMessage && chromeApi?.runtime?.connect
|
|
25
|
+
&& environment.location?.protocol === 'https:'
|
|
26
|
+
&& ['overleaf.com', 'www.overleaf.com', 'cn.overleaf.com'].includes(environment.location?.hostname)
|
|
27
|
+
&& /^\/project(?:\/|$)/.test(environment.location?.pathname || ''));
|
|
28
|
+
}
|
|
29
|
+
function capture(projectId = '', accountScopeId) {
|
|
30
|
+
const current = environment.codexOverleafDeriveAccountScopeId?.();
|
|
31
|
+
if (!current || (accountScopeId && current !== accountScopeId))
|
|
32
|
+
throw failure('account_scope_unavailable', 'A stable matching Overleaf account is required for shared history.');
|
|
33
|
+
return { accountScopeId: current, projectId };
|
|
34
|
+
}
|
|
35
|
+
function assertScope(value) { capture(value.projectId, value.accountScopeId); }
|
|
36
|
+
const cacheKey = (value, id) => value.accountScopeId + ':' + id;
|
|
37
|
+
const lockKey = (kind, value) => kind === 'review' ? 'review:' + JSON.stringify([value.accountScopeId, value.projectId, value.runId]) : kind === 'preferences' ? 'preferences' : 'state:' + value.accountScopeId;
|
|
38
|
+
async function request(method, value, args = {}) {
|
|
39
|
+
assertScope(value);
|
|
40
|
+
const lock = heldLocks.get(lockKey('state', value));
|
|
41
|
+
if (lock?.lost) throw failure('storage_lock_lost', 'The background storage connection was lost.');
|
|
42
|
+
const response = await chromeApi.runtime.sendMessage({
|
|
43
|
+
...args, type: MESSAGE, method, scope: value, clientId, lockToken: lock?.token || ''
|
|
44
|
+
});
|
|
45
|
+
assertScope(value);
|
|
46
|
+
if (!response?.ok)
|
|
47
|
+
throw failure(response?.error?.code || 'shared_store_unavailable',
|
|
48
|
+
response?.error?.message || 'Shared history is unavailable. Reload the extension and this page.');
|
|
49
|
+
return response.result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Live ports own the locks. Worker/page termination releases them without
|
|
53
|
+
// allowing a stale client to silently fall back to an origin-local lock.
|
|
54
|
+
async function acquire(kind, value, work, onLost) {
|
|
55
|
+
assertScope(value);
|
|
56
|
+
const key = lockKey(kind, value), port = chromeApi.runtime.connect({ name: LOCK_PORT });
|
|
57
|
+
let timer, heartbeat, released = false, lock = null, rejectGrant;
|
|
58
|
+
const granted = new Promise((resolve, reject) => {
|
|
59
|
+
rejectGrant = reject;
|
|
60
|
+
port.onMessage.addListener(message => {
|
|
61
|
+
if (message?.type === 'busy') { reject(failure('review_busy', 'Another page is reviewing this run. Wait for it to finish.')); return; }
|
|
62
|
+
if (message?.type !== 'granted' || lock || released) return;
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
lock = { token: message.token, deadlineAt: message.deadlineAt, lost: false };
|
|
65
|
+
heldLocks.set(key, lock);
|
|
66
|
+
resolve();
|
|
67
|
+
});
|
|
68
|
+
port.onDisconnect.addListener(() => {
|
|
69
|
+
// Read lastError to consume the runtime's disconnected-port warning.
|
|
70
|
+
void chromeApi.runtime.lastError;
|
|
71
|
+
if (lock) { lock.lost = true; if (!released) onLost?.(lock.token); }
|
|
72
|
+
if (!released) reject(failure('storage_lock_lost', 'The shared history lock disconnected.'));
|
|
73
|
+
});
|
|
74
|
+
timer = environment.setTimeout(() => reject(failure('storage_lock_timeout',
|
|
75
|
+
'Another page is saving history. Retry after it finishes.')), 20000);
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
heartbeat = environment.setInterval(() => {
|
|
79
|
+
try { port.postMessage({ type: 'ping' }); }
|
|
80
|
+
catch (_error) {
|
|
81
|
+
if (lock) lock.lost = true;
|
|
82
|
+
rejectGrant(failure('storage_lock_lost', 'The shared history lock disconnected.'));
|
|
83
|
+
}
|
|
84
|
+
}, 15000);
|
|
85
|
+
port.postMessage({ type: 'acquire', kind, scope: value });
|
|
86
|
+
await granted;
|
|
87
|
+
assertScope(value);
|
|
88
|
+
const assertOwner = () => { assertScope(value); if (lock.lost) throw failure('storage_lock_lost', 'The shared review owner disconnected.'); };
|
|
89
|
+
assertOwner.token = lock.token;
|
|
90
|
+
assertOwner.markDispatched = async () => {
|
|
91
|
+
assertOwner();
|
|
92
|
+
const lease = await request('review-dispatched', value, { runId: value.runId, reviewToken: lock.token });
|
|
93
|
+
assertOwner(); assertOwner.deadlineAt = lease.deadlineAt; return lease.deadlineAt;
|
|
94
|
+
};
|
|
95
|
+
const result = await work(assertOwner);
|
|
96
|
+
assertScope(value);
|
|
97
|
+
if (lock.lost) throw failure('storage_lock_lost', 'The shared history lock was lost before completion.');
|
|
98
|
+
return result;
|
|
99
|
+
} finally {
|
|
100
|
+
released = true;
|
|
101
|
+
if (kind === 'review' && lock && !lock.lost) {
|
|
102
|
+
await new Promise(resolve => {
|
|
103
|
+
const done = message => { if (message?.type === 'released') { port.onMessage.removeListener(done); environment.clearTimeout(releaseTimer); resolve(); } };
|
|
104
|
+
const releaseTimer = environment.setTimeout(resolve, 2000);
|
|
105
|
+
port.onMessage.addListener(done);
|
|
106
|
+
try { port.postMessage({ type: 'release-review', token: lock.token }); } catch (_) { resolve(); }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
environment.clearTimeout(timer);
|
|
110
|
+
environment.clearInterval(heartbeat);
|
|
111
|
+
if (heldLocks.get(key) === lock) heldLocks.delete(key);
|
|
112
|
+
port.disconnect();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function migrate(value) {
|
|
116
|
+
const account = value.accountScopeId;
|
|
117
|
+
if (!migrations.has(account)) {
|
|
118
|
+
const pending = (async () => {
|
|
119
|
+
const status = await request('migration-status', value);
|
|
120
|
+
if (status.complete) return;
|
|
121
|
+
if (!legacy) throw failure('shared_store_unavailable', 'Legacy history storage is not ready.');
|
|
122
|
+
// Read the original site DB once. Only explicitly owned records may
|
|
123
|
+
// cross origins. Unknown-account rows remain untouched for recovery.
|
|
124
|
+
const records = (await legacy.getAllSessions())
|
|
125
|
+
.filter(record => record?.accountScopeId === account && record.projectId && record.id);
|
|
126
|
+
await acquire('state', value, async () => {
|
|
127
|
+
for (const record of records) {
|
|
128
|
+
const recordScope = capture(record.projectId, account);
|
|
129
|
+
await request('import', recordScope, {
|
|
130
|
+
record: legacy.buildSessionRecord(record, { preserveRunActionPayload: true })
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
await request('migration-complete', value);
|
|
134
|
+
});
|
|
135
|
+
})();
|
|
136
|
+
migrations.set(account, pending);
|
|
137
|
+
pending.catch(() => { if (migrations.get(account) === pending) migrations.delete(account); });
|
|
138
|
+
}
|
|
139
|
+
await migrations.get(account);
|
|
140
|
+
assertScope(value);
|
|
141
|
+
}
|
|
142
|
+
async function withLock(kind, input, work) {
|
|
143
|
+
const value = capture(input.projectId, input.accountScopeId);
|
|
144
|
+
await migrate(value); // Before acquiring either lock: no migration/lock cycle.
|
|
145
|
+
return acquire(kind, value, work);
|
|
146
|
+
}
|
|
147
|
+
async function withWrite(value, work) {
|
|
148
|
+
await migrate(value);
|
|
149
|
+
const held = heldLocks.get(lockKey('state', value));
|
|
150
|
+
if (held) {
|
|
151
|
+
if (held.lost) throw failure('storage_lock_lost', 'The shared history lock was lost.');
|
|
152
|
+
return work();
|
|
153
|
+
}
|
|
154
|
+
return acquire('state', value, work);
|
|
155
|
+
}
|
|
156
|
+
function tag(record, value, adopt) {
|
|
157
|
+
if (!record) return record;
|
|
158
|
+
const base = clean(record);
|
|
159
|
+
if (adopt) baselines.set(cacheKey(value, record.id), base);
|
|
160
|
+
Object.defineProperty(record, BASE, { value: base, enumerable: true });
|
|
161
|
+
return record;
|
|
162
|
+
}
|
|
163
|
+
async function list(projectId = '', adopt = true) {
|
|
164
|
+
const value = capture(projectId);
|
|
165
|
+
await migrate(value);
|
|
166
|
+
const result = []; let after = '';
|
|
167
|
+
do {
|
|
168
|
+
const page = await request('list', value, { after });
|
|
169
|
+
for (const record of page.records) {
|
|
170
|
+
result.push(tag(record, value, adopt && !heldLocks.has(lockKey('state', value))));
|
|
171
|
+
}
|
|
172
|
+
after = page.next;
|
|
173
|
+
} while (after);
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
function adopt(records) {
|
|
177
|
+
const value = capture();
|
|
178
|
+
for (const record of records) {
|
|
179
|
+
if (record.accountScopeId === value.accountScopeId)
|
|
180
|
+
baselines.set(cacheKey(value, record.id), clean(record));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function hasLocalChanges(session) {
|
|
184
|
+
const base = baselines.get(cacheKey(capture(), session.id));
|
|
185
|
+
return Boolean(base && (session.updatedAt !== base.updatedAt || session.task !== base.task));
|
|
186
|
+
}
|
|
187
|
+
function wasStored(id) { return baselines.has(cacheKey(capture(), id)); }
|
|
188
|
+
async function put(records, options = {}) {
|
|
189
|
+
if (!records.length) return [];
|
|
190
|
+
const value = capture(records[0].projectId, records[0].accountScopeId);
|
|
191
|
+
if (records.some(record => record.projectId !== value.projectId || record.accountScopeId !== value.accountScopeId))
|
|
192
|
+
throw failure('shared_store_scope_mismatch', 'A session batch must belong to one account and project.');
|
|
193
|
+
return withWrite(value, async () => {
|
|
194
|
+
const entries = records.map(record => ({
|
|
195
|
+
record: clean(record),
|
|
196
|
+
base: record[BASE] || baselines.get(cacheKey(value, record.id)) || null
|
|
197
|
+
}));
|
|
198
|
+
const transfer = await Transfer.encode(entries);
|
|
199
|
+
const written = await request(transfer.method, value, { ...transfer.payload, reviewRunIds: options.reviewRunIds || [] });
|
|
200
|
+
// Keep the caller's submitted view as its merge baseline. A merged
|
|
201
|
+
// remote change has not necessarily been applied to the visible UI.
|
|
202
|
+
for (const entry of entries) baselines.set(cacheKey(value, entry.record.id), clean(entry.record));
|
|
203
|
+
return written;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
async function get(id) {
|
|
207
|
+
const value = capture();
|
|
208
|
+
await migrate(value);
|
|
209
|
+
const record = await request('get', value, { id });
|
|
210
|
+
return tag(record, value, !heldLocks.has(lockKey('state', value)));
|
|
211
|
+
}
|
|
212
|
+
async function erase(method, projectId = '', id, accountScopeId) {
|
|
213
|
+
const value = capture(projectId, accountScopeId);
|
|
214
|
+
return withWrite(value, () => request(method, value, { id }));
|
|
215
|
+
}
|
|
216
|
+
function decorate(api) {
|
|
217
|
+
legacy = api;
|
|
218
|
+
wrapped = { ...api };
|
|
219
|
+
const route = (name, shared) => {
|
|
220
|
+
wrapped[name] = function (...args) {
|
|
221
|
+
return enabled() ? shared(...args) : api[name](...args);
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
route('buildSessionRecord', (input, options) => {
|
|
225
|
+
const record = api.buildSessionRecord(input, options);
|
|
226
|
+
const base = input[BASE] || baselines.get(cacheKey({ accountScopeId: record.accountScopeId }, record.id));
|
|
227
|
+
if (base) Object.defineProperty(record, BASE, { value: clone(base), enumerable: true });
|
|
228
|
+
return record;
|
|
229
|
+
});
|
|
230
|
+
route('getAllSessions', () => list());
|
|
231
|
+
route('getRecord', (store, id) => store === 'sessions' ? get(id) : api.getRecord(store, id));
|
|
232
|
+
route('getAllByIndex', async (store, index, value) => {
|
|
233
|
+
if (store !== 'sessions') return api.getAllByIndex(store, index, value);
|
|
234
|
+
const records = await list(index === 'projectId' ? value : '');
|
|
235
|
+
return records.filter(record => record[index] === value);
|
|
236
|
+
});
|
|
237
|
+
route('claimSessionsForAccount', async (projectId, accountScopeId, deleted = []) => {
|
|
238
|
+
capture(projectId, accountScopeId);
|
|
239
|
+
return (await list(projectId)).filter(record => !deleted.includes(record.id));
|
|
240
|
+
});
|
|
241
|
+
route('putRecord', async (store, record) =>
|
|
242
|
+
store === 'sessions' ? (await put([record]))[0] : api.putRecord(store, record));
|
|
243
|
+
route('putRecords', (store, records, options) =>
|
|
244
|
+
store === 'sessions' ? put(records, options) : api.putRecords(store, records));
|
|
245
|
+
route('deleteRecord', (store, id, expectedScope) =>
|
|
246
|
+
store === 'sessions' ? erase('delete', expectedScope?.projectId || '', id, expectedScope?.accountScopeId)
|
|
247
|
+
: api.deleteRecord(store, id));
|
|
248
|
+
route('deleteByIndex', async (store, index, value) => {
|
|
249
|
+
if (store !== 'sessions') return api.deleteByIndex(store, index, value);
|
|
250
|
+
if (index === 'projectId') return erase('clear-project', value);
|
|
251
|
+
for (const record of (await list()).filter(record => record[index] === value))
|
|
252
|
+
await erase('delete', record.projectId, record.id);
|
|
253
|
+
});
|
|
254
|
+
route('clearStore', store =>
|
|
255
|
+
store === 'sessions' ? erase('clear-account') : api.clearStore(store));
|
|
256
|
+
route('clearAllStores', async () => {
|
|
257
|
+
const value = capture();
|
|
258
|
+
await erase('clear-account');
|
|
259
|
+
// Preserve other accounts' legacy backups. Auxiliary stores remain
|
|
260
|
+
// site-local and retain their existing explicit clear-all semantics.
|
|
261
|
+
for (const record of await api.getAllSessions()) {
|
|
262
|
+
if (record.accountScopeId === value.accountScopeId) await api.deleteRecord('sessions', record.id);
|
|
263
|
+
}
|
|
264
|
+
await api.clearAuxiliaryStores();
|
|
265
|
+
baselines.clear();
|
|
266
|
+
});
|
|
267
|
+
route('listRecentProjectsAcrossAccount', async options => {
|
|
268
|
+
if (!options?.accountScopeId) return [];
|
|
269
|
+
capture('', options.accountScopeId);
|
|
270
|
+
return api.filterRecentProjectsAcrossAccount(await list(), options);
|
|
271
|
+
});
|
|
272
|
+
Object.assign(wrapped, {
|
|
273
|
+
sharedSessionsEnabled: enabled,
|
|
274
|
+
withSharedReviewLock: async (input, work) => {
|
|
275
|
+
const value = { ...capture(input.projectId, input.accountScopeId), runId: input.runId };
|
|
276
|
+
return acquire('review', value, work, input.onLost);
|
|
277
|
+
},
|
|
278
|
+
hasSharedReviewOwner: input => request('review-owner', capture(input.projectId, input.accountScopeId), { runId: input.runId }),
|
|
279
|
+
withSharedLock: withLock,
|
|
280
|
+
readSharedProjectSnapshot: projectId => list(projectId, false),
|
|
281
|
+
adoptSharedSessionBaseline: adopt,
|
|
282
|
+
hasUnpersistedSharedSession: hasLocalChanges,
|
|
283
|
+
wasSharedSessionStored: wasStored
|
|
284
|
+
});
|
|
285
|
+
return wrapped;
|
|
286
|
+
}
|
|
287
|
+
function subscribe(listener) {
|
|
288
|
+
if (!enabled() || !chromeApi.storage?.onChanged) return () => {};
|
|
289
|
+
const onChange = (changes, area) => {
|
|
290
|
+
const change = changes[CHANGE_KEY]?.newValue;
|
|
291
|
+
if (area !== 'local' || !change || change.clientId === clientId) return;
|
|
292
|
+
const account = environment.codexOverleafDeriveAccountScopeId?.();
|
|
293
|
+
if (account && change.accountScopeId === account) listener(change);
|
|
294
|
+
};
|
|
295
|
+
chromeApi.storage.onChanged.addListener(onChange);
|
|
296
|
+
return () => chromeApi.storage.onChanged.removeListener(onChange);
|
|
297
|
+
}
|
|
298
|
+
return { enabled, decorate, withLock, subscribe,
|
|
299
|
+
async readMeta(input) {
|
|
300
|
+
const value = capture(input.projectId, input.accountScopeId);
|
|
301
|
+
await migrate(value);
|
|
302
|
+
return request('meta-read', value);
|
|
303
|
+
},
|
|
304
|
+
async writeMeta(input, meta) {
|
|
305
|
+
const value = capture(input.projectId, input.accountScopeId);
|
|
306
|
+
return request('meta-write', value, { meta });
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return { createBridge, ...createBridge() };
|
|
311
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) module.exports = factory();
|
|
3
|
+
else root.CodexOverleafModuleRegistry.define('SharedSessionTransfer', [], factory);
|
|
4
|
+
})(typeof globalThis !== 'undefined' ? globalThis : window, function () {
|
|
5
|
+
'use strict';
|
|
6
|
+
const MAX_WIRE_BYTES = 24 * 1024 * 1024;
|
|
7
|
+
// The old UTF-16 estimate admitted up to 3 UTF-8 bytes per code unit.
|
|
8
|
+
// Retain that finite Unicode window without enlarging ordinary ASCII writes.
|
|
9
|
+
const MAX_RECORD_BYTES = MAX_WIRE_BYTES * 3 / 2;
|
|
10
|
+
const MAX_RESPONSE_BYTES = MAX_RECORD_BYTES;
|
|
11
|
+
const MAX_DECODED_BYTES = 2 * MAX_RECORD_BYTES + 64 * 1024;
|
|
12
|
+
const error = (code, message) => Object.assign(new Error(message), { code });
|
|
13
|
+
const tooLarge = () => error('shared_session_too_large', 'The session batch exceeds the bounded shared storage transfer limit.');
|
|
14
|
+
const invalid = () => error('shared_session_transfer_invalid', 'The shared history transfer is invalid or incomplete.');
|
|
15
|
+
const byteLength = text => new TextEncoder().encode(text).byteLength;
|
|
16
|
+
|
|
17
|
+
function serialize(value, limit) {
|
|
18
|
+
// Count a lower bound during serialization, before a large aggregate string
|
|
19
|
+
// can be produced. The final UTF-8 check also includes commas and escaping.
|
|
20
|
+
let lowerBound = 0;
|
|
21
|
+
const text = JSON.stringify(value, function (key, item) {
|
|
22
|
+
if (key && !Array.isArray(this)) lowerBound += byteLength(JSON.stringify(key)) + 1;
|
|
23
|
+
if (typeof item === 'string') {
|
|
24
|
+
if (item.length > limit) throw tooLarge();
|
|
25
|
+
lowerBound += byteLength(JSON.stringify(item));
|
|
26
|
+
} else if (item === null) lowerBound += 4;
|
|
27
|
+
else if (typeof item === 'number') lowerBound += Number.isFinite(item) ? String(item).length : 4;
|
|
28
|
+
else if (typeof item === 'boolean') lowerBound += String(item).length;
|
|
29
|
+
else if (item && typeof item === 'object') lowerBound += 2;
|
|
30
|
+
if (lowerBound > limit) throw tooLarge();
|
|
31
|
+
return item;
|
|
32
|
+
});
|
|
33
|
+
if (typeof text !== 'string') throw invalid();
|
|
34
|
+
const bytes = byteLength(text);
|
|
35
|
+
if (bytes > limit) throw tooLarge();
|
|
36
|
+
return { text, bytes };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validateRecord(record) {
|
|
40
|
+
const value = serialize(record, MAX_RECORD_BYTES);
|
|
41
|
+
if (value.bytes > MAX_WIRE_BYTES && value.text.length * 2 > MAX_WIRE_BYTES) throw tooLarge();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateResponse(records) { serialize(records, MAX_RESPONSE_BYTES); }
|
|
45
|
+
|
|
46
|
+
function validateEntries(entries) {
|
|
47
|
+
if (!Array.isArray(entries)) throw invalid();
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry || !entry.record || typeof entry.record !== 'object' || Array.isArray(entry.record)) throw invalid();
|
|
50
|
+
validateRecord(entry.record);
|
|
51
|
+
if (entry.base != null) {
|
|
52
|
+
if (typeof entry.base !== 'object' || Array.isArray(entry.base)) throw invalid();
|
|
53
|
+
// A previously stored merged baseline may be larger than new records;
|
|
54
|
+
// allow a bounded old base so a smaller candidate can repair it.
|
|
55
|
+
serialize(entry.base, MAX_DECODED_BYTES);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function readLimited(stream, limit) {
|
|
61
|
+
const reader = stream.getReader(), chunks = [];
|
|
62
|
+
let length = 0;
|
|
63
|
+
try {
|
|
64
|
+
while (true) {
|
|
65
|
+
const result = await reader.read();
|
|
66
|
+
if (result.done) break;
|
|
67
|
+
length += result.value.byteLength;
|
|
68
|
+
if (length > limit) { await reader.cancel().catch(() => {}); throw tooLarge(); }
|
|
69
|
+
chunks.push(result.value);
|
|
70
|
+
}
|
|
71
|
+
} finally { reader.releaseLock(); }
|
|
72
|
+
const output = new Uint8Array(length); let offset = 0;
|
|
73
|
+
for (const chunk of chunks) { output.set(chunk, offset); offset += chunk.byteLength; }
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function toBase64(bytes) {
|
|
78
|
+
let text = '';
|
|
79
|
+
for (let i = 0; i < bytes.length; i += 0x8000) text += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
80
|
+
return btoa(text);
|
|
81
|
+
}
|
|
82
|
+
function fromBase64(value) {
|
|
83
|
+
if (typeof value !== 'string' || !value.length || value.length % 4
|
|
84
|
+
|| !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) throw invalid();
|
|
85
|
+
const text = atob(value);
|
|
86
|
+
const bytes = new Uint8Array(text.length);
|
|
87
|
+
for (let i = 0; i < text.length; i++) bytes[i] = text.charCodeAt(i);
|
|
88
|
+
return bytes;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function encode(entries, environment = globalThis) {
|
|
92
|
+
validateEntries(entries);
|
|
93
|
+
const raw = serialize(entries, MAX_DECODED_BYTES);
|
|
94
|
+
if (raw.bytes <= MAX_WIRE_BYTES || (raw.bytes <= MAX_RECORD_BYTES && raw.text.length * 2 <= MAX_WIRE_BYTES)) {
|
|
95
|
+
return { method: 'put', payload: { entries } };
|
|
96
|
+
}
|
|
97
|
+
if (typeof environment.CompressionStream !== 'function') {
|
|
98
|
+
throw error('shared_session_compression_unavailable',
|
|
99
|
+
'Lossless transfer compression is unavailable. History remains in memory; retry with a supported browser.');
|
|
100
|
+
}
|
|
101
|
+
const input = new Blob([raw.text]).stream();
|
|
102
|
+
const packed = await readLimited(input.pipeThrough(new environment.CompressionStream('gzip')),
|
|
103
|
+
Math.floor((MAX_WIRE_BYTES - 1024) * 3 / 4));
|
|
104
|
+
const payload = { encoding: 'gzip-base64', version: 1, decodedBytes: raw.bytes, data: toBase64(packed) };
|
|
105
|
+
serialize(payload, MAX_WIRE_BYTES);
|
|
106
|
+
// A distinct method makes an older worker reject rather than acknowledge
|
|
107
|
+
// an absent `entries` array as a successful empty write.
|
|
108
|
+
return { method: 'put-compressed', payload };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function decode(message, environment = globalThis) {
|
|
112
|
+
if (message.method !== 'put-compressed') {
|
|
113
|
+
const entries = message.method === 'import' ? [{ record: message.record }] : message.entries || [];
|
|
114
|
+
const raw = serialize(entries, MAX_RECORD_BYTES);
|
|
115
|
+
if (raw.bytes > MAX_WIRE_BYTES && raw.text.length * 2 > MAX_WIRE_BYTES) throw tooLarge();
|
|
116
|
+
validateEntries(entries); return entries;
|
|
117
|
+
}
|
|
118
|
+
const payload = { encoding: message.encoding, version: message.version, decodedBytes: message.decodedBytes, data: message.data };
|
|
119
|
+
serialize(payload, MAX_WIRE_BYTES);
|
|
120
|
+
if (message.entries != null || payload.encoding !== 'gzip-base64' || payload.version !== 1
|
|
121
|
+
|| !Number.isSafeInteger(payload.decodedBytes) || payload.decodedBytes < 1 || payload.decodedBytes > MAX_DECODED_BYTES) throw invalid();
|
|
122
|
+
if (typeof environment.DecompressionStream !== 'function') throw error('shared_session_compression_unavailable',
|
|
123
|
+
'Lossless transfer decompression is unavailable. Reload the extension and retry.');
|
|
124
|
+
let bytes;
|
|
125
|
+
try {
|
|
126
|
+
bytes = await readLimited(new Blob([fromBase64(payload.data)]).stream()
|
|
127
|
+
.pipeThrough(new environment.DecompressionStream('gzip')), MAX_DECODED_BYTES);
|
|
128
|
+
} catch (failure) {
|
|
129
|
+
if (['shared_session_too_large', 'shared_session_transfer_invalid'].includes(failure?.code)) throw failure;
|
|
130
|
+
throw invalid();
|
|
131
|
+
}
|
|
132
|
+
if (bytes.byteLength !== payload.decodedBytes) throw invalid();
|
|
133
|
+
let entries;
|
|
134
|
+
try { entries = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes)); }
|
|
135
|
+
catch (_error) { throw invalid(); }
|
|
136
|
+
validateEntries(entries);
|
|
137
|
+
return entries;
|
|
138
|
+
}
|
|
139
|
+
return { MAX_WIRE_BYTES, MAX_RECORD_BYTES, MAX_RESPONSE_BYTES, MAX_DECODED_BYTES, byteLength, validateRecord, validateResponse, encode, decode };
|
|
140
|
+
});
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
if (typeof file?.path === 'string') {
|
|
18
18
|
const path = normalizePath(file.path);
|
|
19
19
|
if (path) {
|
|
20
|
-
|
|
20
|
+
// Patch offsets refer to this exact UTF-16 string. Normalize only
|
|
21
|
+
// for comparisons, never discard the source coordinate system.
|
|
22
|
+
lookup.set(path, String(file.content ?? ''));
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -40,13 +42,7 @@
|
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
const baseContent = baseFileLookup.get(filePath);
|
|
44
|
-
if (!baseContent || baseContent.trim().length === 0) {
|
|
45
|
-
if (typeof console !== 'undefined') {
|
|
46
|
-
console.warn('[codex-overleaf] staleGuard: base empty for', filePath, '→ skipping check');
|
|
47
|
-
}
|
|
48
|
-
return { ok: true };
|
|
49
|
-
}
|
|
45
|
+
const baseContent = normalizeText(baseFileLookup.get(filePath));
|
|
50
46
|
const current = normalizeText(currentContent);
|
|
51
47
|
if (current !== baseContent) {
|
|
52
48
|
if (typeof console !== 'undefined') {
|
|
@@ -55,7 +51,7 @@
|
|
|
55
51
|
'base[0..80]:', JSON.stringify(baseContent.slice(0, 80)),
|
|
56
52
|
'current[0..80]:', JSON.stringify(current.slice(0, 80)));
|
|
57
53
|
}
|
|
58
|
-
const patchRangeFreshness = checkPatchRangeFreshness(operation,
|
|
54
|
+
const patchRangeFreshness = checkPatchRangeFreshness(operation, currentContent, baseFileLookup);
|
|
59
55
|
if (patchRangeFreshness) {
|
|
60
56
|
return patchRangeFreshness;
|
|
61
57
|
}
|
|
@@ -71,13 +67,17 @@
|
|
|
71
67
|
return { ok: true };
|
|
72
68
|
}
|
|
73
69
|
|
|
74
|
-
function checkPatchRangeFreshness(operation, currentContent) {
|
|
75
|
-
const
|
|
70
|
+
function checkPatchRangeFreshness(operation, currentContent, baseFileLookup) {
|
|
71
|
+
const prepared = prepareOperationPatches(operation, currentContent, baseFileLookup);
|
|
72
|
+
if (!prepared.ok) {
|
|
73
|
+
return { ...prepared, code: 'stale_patch_range', reasonKey: 'stalePatchConflict' };
|
|
74
|
+
}
|
|
75
|
+
const patches = prepared.patches;
|
|
76
76
|
if (!patches.length) {
|
|
77
77
|
return null;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
const current =
|
|
80
|
+
const current = String(currentContent ?? '');
|
|
81
81
|
for (const rawPatch of patches) {
|
|
82
82
|
const from = Number(rawPatch?.from);
|
|
83
83
|
const to = Number(rawPatch?.to);
|
|
@@ -107,12 +107,116 @@
|
|
|
107
107
|
};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// Bridge the raw Native baseline to the editor's line-ending representation.
|
|
111
|
+
// Logical LF offsets are only an intermediate coordinate system: both the
|
|
112
|
+
// range check and the eventual editor dispatch use the same raw patches.
|
|
113
|
+
function prepareOperationPatches(operation, currentContent, baseFileLookup) {
|
|
114
|
+
const patches = Array.isArray(operation?.patches) ? operation.patches : [];
|
|
115
|
+
const current = String(currentContent ?? '');
|
|
116
|
+
const path = normalizePath(operation?.path);
|
|
117
|
+
const base = baseFileLookup?.get(path);
|
|
118
|
+
if (!patches.length || typeof base !== 'string' || base === current) {
|
|
119
|
+
return { ok: true, patches };
|
|
120
|
+
}
|
|
121
|
+
const baseMap = lineEndingMap(base);
|
|
122
|
+
const currentMap = lineEndingMap(current);
|
|
123
|
+
// With no CRLF on either side, retain the existing raw range semantics.
|
|
124
|
+
if (!baseMap.removed.length && !currentMap.removed.length) return { ok: true, patches };
|
|
125
|
+
|
|
126
|
+
const groups = [];
|
|
127
|
+
let previousTo = 0;
|
|
128
|
+
for (const patch of patches) {
|
|
129
|
+
const from = Number(patch?.from), to = Number(patch?.to);
|
|
130
|
+
if (!Number.isInteger(from) || !Number.isInteger(to) || from < previousTo || to < from || to > base.length) {
|
|
131
|
+
return patchMappingFailure('invalid_patch');
|
|
132
|
+
}
|
|
133
|
+
if (base.slice(from, to) !== String(patch.expected ?? '')) return patchMappingFailure('stale_patch');
|
|
134
|
+
previousTo = to;
|
|
135
|
+
// Expand only a split CRLF or a new pair formed across an edit edge.
|
|
136
|
+
// Other neighbouring newlines belong to the editor and stay untouched.
|
|
137
|
+
let start = from, end = to;
|
|
138
|
+
const insert = String(patch.insert ?? '');
|
|
139
|
+
if (base[from - 1] === '\r' && (base[from] === '\n' || insert.startsWith('\n') || (!insert && base[to] === '\n'))) start -= 1;
|
|
140
|
+
if (base[to] === '\n' && (base[to - 1] === '\r' || insert.endsWith('\r') || (!insert && base[from - 1] === '\r'))) end += 1;
|
|
141
|
+
const previous = groups[groups.length - 1];
|
|
142
|
+
if (previous && start <= previous.end) {
|
|
143
|
+
previous.end = Math.max(previous.end, end);
|
|
144
|
+
previous.patches.push({ ...patch, from, to });
|
|
145
|
+
} else groups.push({ start, end, patches: [{ ...patch, from, to }] });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const mapped = [], sourceChanges = [];
|
|
149
|
+
for (const group of groups) {
|
|
150
|
+
const logicalFrom = baseMap.toLogical(group.start), logicalTo = baseMap.toLogical(group.end);
|
|
151
|
+
if (logicalTo > currentMap.length) return patchMappingFailure('invalid_patch');
|
|
152
|
+
const from = currentMap.toRaw(logicalFrom), to = currentMap.toRaw(logicalTo);
|
|
153
|
+
const expected = current.slice(from, to);
|
|
154
|
+
if (normalizeText(expected) !== normalizeText(base.slice(group.start, group.end))) {
|
|
155
|
+
return patchMappingFailure('stale_patch');
|
|
156
|
+
}
|
|
157
|
+
let insert = base.slice(group.start, group.end);
|
|
158
|
+
for (const patch of group.patches.slice().sort((left, right) => right.from - left.from)) {
|
|
159
|
+
if (insert.slice(patch.from - group.start, patch.to - group.start) !== String(patch.expected ?? '')) {
|
|
160
|
+
return patchMappingFailure('stale_patch');
|
|
161
|
+
}
|
|
162
|
+
insert = insert.slice(0, patch.from - group.start) + String(patch.insert ?? '') + insert.slice(patch.to - group.start);
|
|
163
|
+
}
|
|
164
|
+
sourceChanges.push({ from: group.start, to: group.end, insert });
|
|
165
|
+
const sourceEnding = nearestLineEnding(base, group.start);
|
|
166
|
+
const editorEnding = nearestLineEnding(current, from);
|
|
167
|
+
if (sourceEnding && editorEnding && sourceEnding !== editorEnding) {
|
|
168
|
+
insert = normalizeText(insert).replace(/\n/g, editorEnding);
|
|
169
|
+
}
|
|
170
|
+
// Leave equal surrounding text out of the dispatch/Track range.
|
|
171
|
+
let prefix = 0, oldEnd = expected.length, newEnd = insert.length;
|
|
172
|
+
while (prefix < oldEnd && prefix < newEnd && expected[prefix] === insert[prefix]) prefix += 1;
|
|
173
|
+
while (oldEnd > prefix && newEnd > prefix && expected[oldEnd - 1] === insert[newEnd - 1]) { oldEnd -= 1; newEnd -= 1; }
|
|
174
|
+
mapped.push({ from: from + prefix, to: from + oldEnd, expected: expected.slice(prefix, oldEnd), insert: insert.slice(prefix, newEnd) });
|
|
175
|
+
}
|
|
176
|
+
// A later operation in the same batch still addresses the original
|
|
177
|
+
// representation, so advance its baseline separately from editor text.
|
|
178
|
+
let sourceContent = base;
|
|
179
|
+
for (const patch of sourceChanges.reverse()) {
|
|
180
|
+
sourceContent = sourceContent.slice(0, patch.from) + patch.insert + sourceContent.slice(patch.to);
|
|
181
|
+
}
|
|
182
|
+
return { ok: true, patches: mapped, sourceContent };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function patchMappingFailure(code) {
|
|
186
|
+
return { ok: false, code, reason: 'Codex 要修改的位置已经无法和当前 Overleaf 内容对齐,所以没有写入。请重新运行任务。' };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function lineEndingMap(text) {
|
|
190
|
+
const removed = [];
|
|
191
|
+
for (let index = 0; index < text.length - 1; index += 1) {
|
|
192
|
+
if (text[index] === '\r' && text[index + 1] === '\n') removed.push(index);
|
|
193
|
+
}
|
|
194
|
+
function countBefore(offset, logical) {
|
|
195
|
+
let low = 0, high = removed.length;
|
|
196
|
+
while (low < high) {
|
|
197
|
+
const middle = (low + high) >>> 1;
|
|
198
|
+
if (removed[middle] - (logical ? middle : 0) < offset) low = middle + 1;
|
|
199
|
+
else high = middle;
|
|
200
|
+
}
|
|
201
|
+
return low;
|
|
202
|
+
}
|
|
203
|
+
return { removed, length: text.length - removed.length,
|
|
204
|
+
toLogical: offset => offset - countBefore(offset, false),
|
|
205
|
+
toRaw: offset => offset + countBefore(offset, true) };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function nearestLineEnding(text, offset) {
|
|
209
|
+
let newline = text.indexOf('\n', offset);
|
|
210
|
+
if (newline < 0) newline = text.lastIndexOf('\n', offset - 1);
|
|
211
|
+
return newline < 0 ? '' : text[newline - 1] === '\r' ? '\r\n' : '\n';
|
|
212
|
+
}
|
|
213
|
+
|
|
110
214
|
function updateExpectedFileContent(baseFileLookup, filePath, content) {
|
|
111
215
|
const path = normalizePath(filePath);
|
|
112
216
|
if (!baseFileLookup || !path) {
|
|
113
217
|
return;
|
|
114
218
|
}
|
|
115
|
-
baseFileLookup.set(path,
|
|
219
|
+
baseFileLookup.set(path, String(content ?? ''));
|
|
116
220
|
}
|
|
117
221
|
|
|
118
222
|
function removeExpectedFile(baseFileLookup, filePath) {
|
|
@@ -152,6 +256,7 @@
|
|
|
152
256
|
return {
|
|
153
257
|
buildBaseFileLookup,
|
|
154
258
|
checkOperationFreshness,
|
|
259
|
+
prepareOperationPatches,
|
|
155
260
|
updateExpectedFileContent,
|
|
156
261
|
removeExpectedFile,
|
|
157
262
|
moveExpectedFile,
|