codex-overleaf-link 2.2.0 → 2.3.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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +53 -69
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/contentRuntime.js +650 -1449
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41370 -0
- package/extension/src/content/generated/content.bundle.meta.json +102 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +5 -7
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +7 -5
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +11 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +91 -23
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +6 -1
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +8 -5
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +177 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +168 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +181 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +238 -0
- package/extension/src/shared/sessionState.js +64 -48
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +19 -5
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +143 -0
- package/extension/src/shared/writebackSettlement.js +705 -0
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/mirrorWorkspace.js +15 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +3 -3
- package/package.json +13 -5
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const PAGE_BRIDGE_INSTALL_VERSION = window.CodexOverleafCompatibility?.BUILD_TARGET_VERSION || 'dev';
|
|
5
|
-
const
|
|
5
|
+
const pageRpcContract = window.CodexOverleafPageRpcContract;
|
|
6
|
+
const PAGE_BRIDGE_INSTALL_REVISION = pageRpcContract.REVISION;
|
|
6
7
|
if (window.__codexOverleafPageBridgeInstalledVersion === PAGE_BRIDGE_INSTALL_VERSION
|
|
7
8
|
&& window.__codexOverleafPageBridgeInstalledRevision === PAGE_BRIDGE_INSTALL_REVISION) {
|
|
8
9
|
return;
|
|
@@ -152,10 +153,7 @@
|
|
|
152
153
|
result = await dispatch(method, params || {});
|
|
153
154
|
}
|
|
154
155
|
} catch (error) {
|
|
155
|
-
result =
|
|
156
|
-
ok: false,
|
|
157
|
-
error: error.message
|
|
158
|
-
};
|
|
156
|
+
result = pageRpcContract.normalizeFailure(method, error);
|
|
159
157
|
}
|
|
160
158
|
|
|
161
159
|
window.postMessage({
|
|
@@ -187,111 +185,62 @@
|
|
|
187
185
|
return writeCancellationSequence;
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
if (method === 'cancelActiveWrite') {
|
|
188
|
+
const pageRpcHandlers = {
|
|
189
|
+
probe,
|
|
190
|
+
cancelActiveWrite() {
|
|
195
191
|
const sequence = bumpWriteCancellationSequence();
|
|
196
192
|
return { ok: true, cancelled: true, sequence };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (method === 'getProjectFileList') {
|
|
202
|
-
return projectSnapshotBridge.getProjectFileList(params);
|
|
203
|
-
}
|
|
204
|
-
if (method === 'invalidateProjectSnapshot') {
|
|
193
|
+
},
|
|
194
|
+
getProjectSnapshot: params => projectSnapshotBridge.getProjectSnapshot(withSnapshotCacheIdentity(params)),
|
|
195
|
+
getProjectFileList: params => projectSnapshotBridge.getProjectFileList(params),
|
|
196
|
+
invalidateProjectSnapshot(params) {
|
|
205
197
|
projectSnapshotBridge.invalidateProjectSnapshot(params);
|
|
206
198
|
return { ok: true };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// Gate the Reviewing-mode toggle with the same writeGuard the per-write
|
|
213
|
-
// path uses. Without this, a mid-flight SPA navigation could leave the
|
|
214
|
-
// pre-flight toggle flipping Track Changes in a different Overleaf
|
|
215
|
-
// project than the run was bound to. params.runProjectId is set by
|
|
216
|
-
// the content-side caller (preflightWriteSafety / ensureReviewingBeforeWrite);
|
|
217
|
-
// when absent (defensive), the guard short-circuits via its own
|
|
218
|
-
// missing-runProjectId branch.
|
|
219
|
-
if (typeof params?.runProjectId === 'string' && params.runProjectId) {
|
|
220
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
221
|
-
if (blocked) return blocked;
|
|
222
|
-
}
|
|
223
|
-
return ensureReviewing(params);
|
|
224
|
-
}
|
|
225
|
-
if (method === 'ensureEditing') {
|
|
226
|
-
if (typeof params?.runProjectId === 'string' && params.runProjectId) {
|
|
227
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
228
|
-
if (blocked) return blocked;
|
|
229
|
-
}
|
|
230
|
-
return ensureEditing(params);
|
|
231
|
-
}
|
|
232
|
-
if (method === 'applyOperations') {
|
|
233
|
-
// Welcome-panel + write-guard:
|
|
234
|
-
// First gate: runProjectId vs page-side editorProjectId. Runs before
|
|
235
|
-
// any reviewing / save-state / open-file readiness check so a
|
|
236
|
-
// mid-run navigation cannot write into a different project. runWriteGuard
|
|
237
|
-
// is async because it retries the page-side editorProjectId reader with
|
|
238
|
-
// backoff to ride out Overleaf's hydration window.
|
|
239
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
240
|
-
if (blocked) return blocked;
|
|
241
|
-
return applyOperations(params.operations || [], {
|
|
199
|
+
},
|
|
200
|
+
createCheckpoint: params => createCheckpoint(params.label),
|
|
201
|
+
ensureReviewing,
|
|
202
|
+
ensureEditing,
|
|
203
|
+
applyOperations: params => applyOperations(params.operations || [], {
|
|
242
204
|
baseFiles: params.baseFiles || null,
|
|
243
205
|
reviewingPolicy: params.reviewingPolicy || '',
|
|
244
206
|
requireReviewing: params.requireReviewing === true,
|
|
245
207
|
requireEditing: params.requireEditing === true,
|
|
246
|
-
// Propagate runProjectId so the writeback router's defense-in-depth
|
|
247
|
-
// missing-runProjectId check sees it on this in-bridge dispatch path.
|
|
248
208
|
runProjectId: typeof params.runProjectId === 'string' ? params.runProjectId : ''
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
209
|
+
}),
|
|
210
|
+
jumpToPosition,
|
|
211
|
+
rejectTrackedChanges,
|
|
212
|
+
acceptTrackedChanges,
|
|
213
|
+
triggerCompile: params => compileBridge.triggerCompile(params),
|
|
214
|
+
getCompileLog: params => compileBridge.getCompileLog(params),
|
|
215
|
+
getCompileState: () => compileBridge.getCompileState(),
|
|
216
|
+
waitForSaveState,
|
|
217
|
+
startOtObserver: params => otObserver.start(params),
|
|
218
|
+
stopOtObserver: () => otObserver.stop(),
|
|
219
|
+
getOtStatus: () => otObserver.getStatus(),
|
|
220
|
+
drainOtEvents() {
|
|
221
|
+
return { ok: true, events: otObserver.drainEvents().map(sanitizeOtEventForContent) };
|
|
258
222
|
}
|
|
259
|
-
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
async function dispatch(method, params) {
|
|
226
|
+
const contract = pageRpcContract.getMethod(method);
|
|
227
|
+
if (!contract || method === PAGE_BRIDGE_CAPABILITY_METHOD) {
|
|
228
|
+
return { ok: false, error: `Unknown page bridge method: ${method}` };
|
|
229
|
+
}
|
|
230
|
+
const handler = pageRpcHandlers[method];
|
|
231
|
+
if (typeof handler !== 'function') {
|
|
232
|
+
return { ok: false, error: `Page bridge handler unavailable: ${method}` };
|
|
233
|
+
}
|
|
234
|
+
const hasRunProjectId = typeof params?.runProjectId === 'string'
|
|
235
|
+
&& params.runProjectId.trim().length > 0;
|
|
236
|
+
if (
|
|
237
|
+
contract.projectIdentity === 'required'
|
|
238
|
+
|| (contract.projectIdentity === 'optional' && hasRunProjectId)
|
|
239
|
+
) {
|
|
260
240
|
const blocked = await writeGuard.runWriteGuard(params);
|
|
261
241
|
if (blocked) return blocked;
|
|
262
|
-
return acceptTrackedChanges(params);
|
|
263
|
-
}
|
|
264
|
-
if (method === 'triggerCompile') {
|
|
265
|
-
return compileBridge.triggerCompile(params);
|
|
266
|
-
}
|
|
267
|
-
if (method === 'getCompileLog') {
|
|
268
|
-
return compileBridge.getCompileLog(params);
|
|
269
|
-
}
|
|
270
|
-
if (method === 'getCompileState') {
|
|
271
|
-
return compileBridge.getCompileState();
|
|
272
|
-
}
|
|
273
|
-
if (method === 'waitForSaveState') {
|
|
274
|
-
return waitForSaveState(params);
|
|
275
242
|
}
|
|
276
|
-
|
|
277
|
-
return otObserver.start(params);
|
|
278
|
-
}
|
|
279
|
-
if (method === 'stopOtObserver') {
|
|
280
|
-
return otObserver.stop();
|
|
281
|
-
}
|
|
282
|
-
if (method === 'getOtStatus') {
|
|
283
|
-
return otObserver.getStatus();
|
|
284
|
-
}
|
|
285
|
-
if (method === 'drainOtEvents') {
|
|
286
|
-
return {
|
|
287
|
-
ok: true,
|
|
288
|
-
events: otObserver.drainEvents().map(sanitizeOtEventForContent)
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
return {
|
|
292
|
-
ok: false,
|
|
293
|
-
error: `Unknown page bridge method: ${method}`
|
|
294
|
-
};
|
|
243
|
+
return handler(params);
|
|
295
244
|
}
|
|
296
245
|
|
|
297
246
|
function sanitizeOtEventForContent(event = {}) {
|
|
@@ -468,7 +417,7 @@
|
|
|
468
417
|
reviewing: window.CodexOverleafReviewing.detectReviewingFromSignals(reviewingSignals),
|
|
469
418
|
reviewingDiagnostics: reviewingSignals.diagnostics,
|
|
470
419
|
editor,
|
|
471
|
-
capabilities: collectPageCapabilities(editor),
|
|
420
|
+
capabilities: pageRpcContract.withCapabilityReport(collectPageCapabilities(editor)),
|
|
472
421
|
editorDiagnostics: buildEditorDiagnostics(editor),
|
|
473
422
|
projectDiagnostics: {
|
|
474
423
|
internalRootKeys: collectInternalRootKeys(),
|
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
getI18n: function getI18nCjs() { return require('./i18n.js'); }
|
|
6
6
|
});
|
|
7
7
|
} else {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
root.CodexOverleafModuleRegistry.define('AgentTranscript', [
|
|
9
|
+
'FailureReasons',
|
|
10
|
+
'I18n'
|
|
11
|
+
], function createAgentTranscript(FailureReasons, I18n) {
|
|
12
|
+
return factory({
|
|
13
|
+
getFailureReasons: function getFailureReasonsWindow() { return FailureReasons; },
|
|
14
|
+
getI18n: function getI18nWindow() { return I18n; }
|
|
15
|
+
});
|
|
13
16
|
});
|
|
14
17
|
}
|
|
15
18
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function agentTranscriptFactory(deps) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
13
|
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
14
|
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
-
const BUILD_TARGET_VERSION = '2.
|
|
15
|
+
const BUILD_TARGET_VERSION = '2.3.0';
|
|
16
16
|
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
17
|
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
18
|
'bridgePing',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
}
|
|
9
9
|
module.exports = factory(projectFiles);
|
|
10
10
|
} else {
|
|
11
|
-
root.
|
|
11
|
+
root.CodexOverleafModuleRegistry.define('GovernanceRules', ['ProjectFiles'], factory);
|
|
12
12
|
}
|
|
13
13
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function governanceRulesFactory(projectFiles) {
|
|
14
14
|
'use strict';
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
(function initCodexOverleafManagedUpdateProjection(root) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const PHASE_ALIASES = Object.freeze({
|
|
5
|
+
waiting: 'waiting_for_idle',
|
|
6
|
+
waiting_for_safe_point: 'waiting_for_idle',
|
|
7
|
+
awaiting_health_check: 'awaiting_health',
|
|
8
|
+
health_checking: 'awaiting_health'
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const PHASES = Object.freeze([
|
|
12
|
+
'idle',
|
|
13
|
+
'up_to_date',
|
|
14
|
+
'checking',
|
|
15
|
+
'update_available',
|
|
16
|
+
'downloading',
|
|
17
|
+
'staged',
|
|
18
|
+
'waiting_for_idle',
|
|
19
|
+
'applying',
|
|
20
|
+
'awaiting_health',
|
|
21
|
+
'committed',
|
|
22
|
+
'failed',
|
|
23
|
+
'rolling_back',
|
|
24
|
+
'rolled_back',
|
|
25
|
+
'deferred'
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const PHASE_TIMEOUTS = Object.freeze({
|
|
29
|
+
checking: 30 * 1000,
|
|
30
|
+
downloading: 2 * 60 * 1000,
|
|
31
|
+
applying: 90 * 1000,
|
|
32
|
+
awaiting_health: 2 * 60 * 1000,
|
|
33
|
+
rolling_back: 60 * 1000
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const PROGRESS = Object.freeze({
|
|
37
|
+
checking: 8,
|
|
38
|
+
update_available: 0,
|
|
39
|
+
downloading: 25,
|
|
40
|
+
staged: 48,
|
|
41
|
+
waiting_for_idle: 55,
|
|
42
|
+
applying: 75,
|
|
43
|
+
awaiting_health: 90,
|
|
44
|
+
committed: 100,
|
|
45
|
+
rolled_back: 100
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const ACTIVE_STAGE = Object.freeze({
|
|
49
|
+
downloading: 0,
|
|
50
|
+
staged: 1,
|
|
51
|
+
waiting_for_idle: 1,
|
|
52
|
+
applying: 2,
|
|
53
|
+
awaiting_health: 2,
|
|
54
|
+
committed: 3
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const ACTIVE_PHASES = Object.freeze({
|
|
58
|
+
update_page: Object.freeze([
|
|
59
|
+
'checking',
|
|
60
|
+
'downloading',
|
|
61
|
+
'staged',
|
|
62
|
+
'waiting_for_idle',
|
|
63
|
+
'applying',
|
|
64
|
+
'awaiting_health'
|
|
65
|
+
]),
|
|
66
|
+
panel: Object.freeze([
|
|
67
|
+
'downloading',
|
|
68
|
+
'staged',
|
|
69
|
+
'waiting_for_idle',
|
|
70
|
+
'applying',
|
|
71
|
+
'awaiting_health'
|
|
72
|
+
])
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const TRANSITIONS = Object.freeze({
|
|
76
|
+
idle: Object.freeze(['checking']),
|
|
77
|
+
up_to_date: Object.freeze(['checking', 'idle']),
|
|
78
|
+
checking: Object.freeze(['idle', 'up_to_date', 'update_available', 'failed']),
|
|
79
|
+
update_available: Object.freeze(['checking', 'downloading', 'deferred', 'failed']),
|
|
80
|
+
downloading: Object.freeze(['staged', 'failed']),
|
|
81
|
+
staged: Object.freeze(['waiting_for_idle', 'applying', 'update_available', 'deferred', 'failed']),
|
|
82
|
+
waiting_for_idle: Object.freeze(['applying', 'update_available', 'deferred', 'failed']),
|
|
83
|
+
applying: Object.freeze(['staged', 'awaiting_health', 'rolling_back', 'failed']),
|
|
84
|
+
awaiting_health: Object.freeze(['staged', 'committed', 'rolling_back', 'failed']),
|
|
85
|
+
committed: Object.freeze(['checking', 'idle']),
|
|
86
|
+
failed: Object.freeze(['checking', 'rolling_back', 'update_available', 'idle']),
|
|
87
|
+
rolling_back: Object.freeze(['rolled_back', 'failed']),
|
|
88
|
+
rolled_back: Object.freeze(['checking', 'idle']),
|
|
89
|
+
deferred: Object.freeze(['checking', 'update_available', 'idle'])
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
function asObject(value) {
|
|
93
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function unwrap(value) {
|
|
97
|
+
const source = asObject(value);
|
|
98
|
+
return source.state && typeof source.state === 'object' && !Array.isArray(source.state)
|
|
99
|
+
? source.state
|
|
100
|
+
: source;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizePhase(value) {
|
|
104
|
+
const normalized = String(value || 'idle').trim().toLowerCase().replace(/[- ]+/g, '_');
|
|
105
|
+
return PHASE_ALIASES[normalized] || normalized || 'idle';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function phaseOf(value) {
|
|
109
|
+
if (typeof value === 'string') {
|
|
110
|
+
return normalizePhase(value);
|
|
111
|
+
}
|
|
112
|
+
const source = unwrap(value);
|
|
113
|
+
return normalizePhase(source.phase || source.status || source.state || source.lifecycle || 'idle');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function normalizeBlockers(values, normalizeFn) {
|
|
117
|
+
if (typeof normalizeFn === 'function') {
|
|
118
|
+
return normalizeFn(values);
|
|
119
|
+
}
|
|
120
|
+
const result = [];
|
|
121
|
+
const seen = new Set();
|
|
122
|
+
for (const value of Array.isArray(values) ? values : [values]) {
|
|
123
|
+
const code = String(value || '').trim();
|
|
124
|
+
if (!code || seen.has(code)) continue;
|
|
125
|
+
seen.add(code);
|
|
126
|
+
result.push(code);
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function normalize(value, options = {}) {
|
|
132
|
+
const source = unwrap(value);
|
|
133
|
+
const currentVersion = String(
|
|
134
|
+
source.currentVersion || options.currentVersion || source.latestVersion || ''
|
|
135
|
+
).replace(/^v/i, '');
|
|
136
|
+
const latestVersion = String(
|
|
137
|
+
source.latestVersion || source.targetVersion || currentVersion
|
|
138
|
+
).replace(/^v/i, '');
|
|
139
|
+
const blockers = normalizeBlockers(
|
|
140
|
+
Array.isArray(source.blockers) ? source.blockers : (source.blocker ? [source.blocker] : []),
|
|
141
|
+
options.normalizeBlockers
|
|
142
|
+
);
|
|
143
|
+
return {
|
|
144
|
+
...source,
|
|
145
|
+
state: phaseOf(source),
|
|
146
|
+
managed: source.managed !== false,
|
|
147
|
+
currentVersion,
|
|
148
|
+
latestVersion,
|
|
149
|
+
etag: String(source.etag || ''),
|
|
150
|
+
lastCheckedAt: Number(source.lastCheckedAt || 0),
|
|
151
|
+
stagedAt: Number(source.stagedAt || 0),
|
|
152
|
+
postponeUntil: Number(source.postponeUntil || 0),
|
|
153
|
+
transactionId: String(source.transactionId || ''),
|
|
154
|
+
blocker: blockers[0] || '',
|
|
155
|
+
blockers,
|
|
156
|
+
code: String(source.code || ''),
|
|
157
|
+
message: String(source.message || '').slice(0, 300),
|
|
158
|
+
phaseStartedAt: Number(source.phaseStartedAt || 0),
|
|
159
|
+
deadlineAt: Number(source.deadlineAt || 0),
|
|
160
|
+
heartbeatAt: Number(source.heartbeatAt || 0)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getPhaseTimeoutMs(value) {
|
|
165
|
+
return Number(PHASE_TIMEOUTS[phaseOf(value)] || 0);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ensurePhaseMetadata(value, options = {}) {
|
|
169
|
+
const normalized = normalize(value, options);
|
|
170
|
+
const phase = phaseOf(normalized);
|
|
171
|
+
const timeoutMs = getPhaseTimeoutMs(phase);
|
|
172
|
+
if (!timeoutMs) {
|
|
173
|
+
return {
|
|
174
|
+
...normalized,
|
|
175
|
+
phaseStartedAt: 0,
|
|
176
|
+
deadlineAt: 0,
|
|
177
|
+
heartbeatAt: 0
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const now = Number(options.now || Date.now());
|
|
181
|
+
const phaseStartedAt = Number(normalized.phaseStartedAt || now);
|
|
182
|
+
return {
|
|
183
|
+
...normalized,
|
|
184
|
+
phaseStartedAt,
|
|
185
|
+
deadlineAt: Number(normalized.deadlineAt || (phaseStartedAt + timeoutMs)),
|
|
186
|
+
heartbeatAt: Number(normalized.heartbeatAt || now)
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function transition(previous, patch, options = {}) {
|
|
191
|
+
const prior = normalize(previous, options);
|
|
192
|
+
const nextSource = buildTransitionSource(prior, patch, options);
|
|
193
|
+
const nextPhase = phaseOf(nextSource);
|
|
194
|
+
const previousPhase = phaseOf(prior);
|
|
195
|
+
if (nextPhase !== previousPhase) {
|
|
196
|
+
nextSource.phaseStartedAt = 0;
|
|
197
|
+
nextSource.deadlineAt = 0;
|
|
198
|
+
nextSource.heartbeatAt = 0;
|
|
199
|
+
}
|
|
200
|
+
const transitioned = ensurePhaseMetadata(nextSource, options);
|
|
201
|
+
if (getPhaseTimeoutMs(nextPhase)) {
|
|
202
|
+
transitioned.heartbeatAt = Number(options.now || Date.now());
|
|
203
|
+
}
|
|
204
|
+
return transitioned;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function transitionCommand(previous, patch, options = {}) {
|
|
208
|
+
const prior = normalize(previous, options);
|
|
209
|
+
const nextSource = buildTransitionSource(prior, patch, options);
|
|
210
|
+
const previousPhase = phaseOf(prior);
|
|
211
|
+
const nextPhase = phaseOf(nextSource);
|
|
212
|
+
if (!isLegalTransition(previousPhase, nextPhase)) {
|
|
213
|
+
const error = new Error(`Illegal managed update transition: ${previousPhase} -> ${nextPhase}`);
|
|
214
|
+
error.code = 'managed_update_transition_invalid';
|
|
215
|
+
error.from = previousPhase;
|
|
216
|
+
error.to = nextPhase;
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
return transition(prior, nextSource, { ...options, merge: false });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildTransitionSource(prior, patch, options = {}) {
|
|
223
|
+
return options.merge === false
|
|
224
|
+
? asObject(patch)
|
|
225
|
+
: { ...prior, ...asObject(patch) };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isLegalTransition(from, to) {
|
|
229
|
+
const fromPhase = phaseOf(from);
|
|
230
|
+
const toPhase = phaseOf(to);
|
|
231
|
+
if (fromPhase === toPhase) return true;
|
|
232
|
+
return (TRANSITIONS[fromPhase] || []).includes(toPhase);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function reconcile(value, transaction, options = {}) {
|
|
236
|
+
const state = normalize(value, options);
|
|
237
|
+
const native = asObject(transaction);
|
|
238
|
+
if (native.state === 'rolled_back') {
|
|
239
|
+
return {
|
|
240
|
+
action: 'reload_tabs',
|
|
241
|
+
state: transition(state, {
|
|
242
|
+
state: 'rolled_back',
|
|
243
|
+
currentVersion: native.sourceVersion || state.currentVersion,
|
|
244
|
+
transactionId: native.id || state.transactionId,
|
|
245
|
+
code: native.reasonCode || state.code || 'update_rolled_back'
|
|
246
|
+
}, options)
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (native.state === 'committed') {
|
|
250
|
+
const targetVersion = native.targetVersion || state.latestVersion || state.currentVersion;
|
|
251
|
+
return {
|
|
252
|
+
action: 'reload_tabs',
|
|
253
|
+
state: transition(state, {
|
|
254
|
+
state: 'committed',
|
|
255
|
+
currentVersion: targetVersion,
|
|
256
|
+
latestVersion: targetVersion,
|
|
257
|
+
transactionId: '',
|
|
258
|
+
blocker: '',
|
|
259
|
+
blockers: [],
|
|
260
|
+
code: '',
|
|
261
|
+
message: ''
|
|
262
|
+
}, options)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (native.state === 'staged' && ['applying', 'awaiting_health'].includes(phaseOf(state))) {
|
|
266
|
+
return {
|
|
267
|
+
action: 'retry_install',
|
|
268
|
+
state: transition(state, {
|
|
269
|
+
state: 'staged',
|
|
270
|
+
transactionId: native.id || state.transactionId,
|
|
271
|
+
blocker: '',
|
|
272
|
+
blockers: [],
|
|
273
|
+
code: '',
|
|
274
|
+
message: ''
|
|
275
|
+
}, options)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (!native.state && ['applying', 'awaiting_health', 'rolling_back'].includes(phaseOf(state))) {
|
|
279
|
+
return {
|
|
280
|
+
action: 'none',
|
|
281
|
+
state: transition(state, {
|
|
282
|
+
state: 'failed',
|
|
283
|
+
code: 'update_transaction_lost',
|
|
284
|
+
message: options.transactionLostMessage
|
|
285
|
+
|| 'The managed update transaction could not be recovered. Use the manual update command.'
|
|
286
|
+
}, options)
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
return { action: 'none', state };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function progressFor(value) {
|
|
293
|
+
const source = asObject(value);
|
|
294
|
+
const raw = asObject(source.progress).percent
|
|
295
|
+
?? asObject(source.progress).value
|
|
296
|
+
?? source.progressPercent
|
|
297
|
+
?? (typeof source.progress === 'number' ? source.progress : undefined);
|
|
298
|
+
if (Number.isFinite(Number(raw))) {
|
|
299
|
+
const numeric = Number(raw);
|
|
300
|
+
return Math.max(0, Math.min(100, numeric <= 1 ? numeric * 100 : numeric));
|
|
301
|
+
}
|
|
302
|
+
return Number(PROGRESS[phaseOf(value)] || 0);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function activeStageFor(value) {
|
|
306
|
+
return ACTIVE_STAGE[phaseOf(value)] ?? -1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function activePhases(surface = 'update_page') {
|
|
310
|
+
return [...(ACTIVE_PHASES[surface] || ACTIVE_PHASES.update_page)];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function isActive(value, surface = 'update_page') {
|
|
314
|
+
return activePhases(surface).includes(phaseOf(value));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
root.CodexOverleafManagedUpdateProjection = Object.freeze({
|
|
318
|
+
ACTIVE_PHASES,
|
|
319
|
+
PHASE_ALIASES,
|
|
320
|
+
PHASE_TIMEOUTS,
|
|
321
|
+
PHASES,
|
|
322
|
+
PROGRESS,
|
|
323
|
+
TRANSITIONS,
|
|
324
|
+
activePhases,
|
|
325
|
+
activeStageFor,
|
|
326
|
+
ensurePhaseMetadata,
|
|
327
|
+
getPhaseTimeoutMs,
|
|
328
|
+
isActive,
|
|
329
|
+
isLegalTransition,
|
|
330
|
+
normalize,
|
|
331
|
+
phaseOf,
|
|
332
|
+
progressFor,
|
|
333
|
+
reconcile,
|
|
334
|
+
transition,
|
|
335
|
+
transitionCommand
|
|
336
|
+
});
|
|
337
|
+
})(typeof window !== 'undefined' ? window : globalThis);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
(function initNativeRequestIdentity(root, factory) {
|
|
2
|
+
const api = factory();
|
|
3
|
+
if (typeof module === "object" && module.exports) {
|
|
4
|
+
module.exports = api;
|
|
5
|
+
}
|
|
6
|
+
if (root) {
|
|
7
|
+
root.CodexOverleafNativeRequestIdentity = api;
|
|
8
|
+
}
|
|
9
|
+
})(typeof globalThis !== "undefined" ? globalThis : this, function createNativeRequestIdentity() {
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
const MAX_REQUEST_ID_CHARS = 160;
|
|
13
|
+
|
|
14
|
+
function invalidIdentity(value) {
|
|
15
|
+
return {
|
|
16
|
+
ok: false,
|
|
17
|
+
error: {
|
|
18
|
+
code: "native_request_id_invalid",
|
|
19
|
+
message:
|
|
20
|
+
`Native request id must be a finite number or a non-empty string no longer than ${MAX_REQUEST_ID_CHARS} characters.`,
|
|
21
|
+
details: {
|
|
22
|
+
actualType: value === null ? "null" : typeof value,
|
|
23
|
+
actualLength: typeof value === "string" ? value.length : null,
|
|
24
|
+
limit: MAX_REQUEST_ID_CHARS
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function validate(value) {
|
|
31
|
+
if (typeof value === "number") {
|
|
32
|
+
return Number.isFinite(value) ? { ok: true, id: value } : invalidIdentity(value);
|
|
33
|
+
}
|
|
34
|
+
if (typeof value !== "string" || value.length === 0 || value.length > MAX_REQUEST_ID_CHARS) {
|
|
35
|
+
return invalidIdentity(value);
|
|
36
|
+
}
|
|
37
|
+
return { ok: true, id: value };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolve(value, randomUUID) {
|
|
41
|
+
if (value === undefined || value === null || value === "") {
|
|
42
|
+
const generate =
|
|
43
|
+
typeof randomUUID === "function"
|
|
44
|
+
? randomUUID
|
|
45
|
+
: typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
|
|
46
|
+
? () => crypto.randomUUID()
|
|
47
|
+
: null;
|
|
48
|
+
if (!generate) {
|
|
49
|
+
return invalidIdentity(value);
|
|
50
|
+
}
|
|
51
|
+
return validate(generate());
|
|
52
|
+
}
|
|
53
|
+
return validate(value);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Object.freeze({
|
|
57
|
+
MAX_REQUEST_ID_CHARS,
|
|
58
|
+
resolve
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -274,28 +274,9 @@
|
|
|
274
274
|
|
|
275
275
|
function hashText(text) {
|
|
276
276
|
const value = String(text ?? '');
|
|
277
|
-
const nodeCrypto = getNodeCrypto();
|
|
278
|
-
if (nodeCrypto) {
|
|
279
|
-
return nodeCrypto.createHash('sha256').update(value, 'utf8').digest('hex');
|
|
280
|
-
}
|
|
281
277
|
return sha256Hex(utf8Bytes(value));
|
|
282
278
|
}
|
|
283
279
|
|
|
284
|
-
function getNodeCrypto() {
|
|
285
|
-
if (typeof require !== 'function') {
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
try {
|
|
289
|
-
return require('node:crypto');
|
|
290
|
-
} catch (error) {
|
|
291
|
-
try {
|
|
292
|
-
return require('crypto');
|
|
293
|
-
} catch (fallbackError) {
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
280
|
function normalizePath(value) {
|
|
300
281
|
return String(value || '')
|
|
301
282
|
.replace(/\s+/g, ' ')
|