codex-overleaf-link 2.2.1 → 2.3.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 +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- 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 +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- 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 +8 -6
- 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 +17 -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 +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- 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 +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- 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 +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- 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/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
var api = factory();
|
|
3
|
+
if (typeof module === 'object' && module.exports) module.exports = api;
|
|
4
|
+
if (root) root.CodexOverleafScopedPersistenceQueuePolicy = api;
|
|
5
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
function createQueueClaim(input) {
|
|
9
|
+
return Object.freeze({
|
|
10
|
+
itemId: cleanPart(input && input.itemId),
|
|
11
|
+
ownerId: cleanPart(input && input.ownerId),
|
|
12
|
+
queueRevision: normalizeRevision(input && input.queueRevision),
|
|
13
|
+
claimedAt: cleanPart(input && input.claimedAt) || new Date().toISOString()
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function isClaimCurrent(claim, queueState) {
|
|
18
|
+
return Boolean(claim && queueState
|
|
19
|
+
&& cleanPart(claim.itemId) === cleanPart(queueState.itemId)
|
|
20
|
+
&& cleanPart(claim.ownerId) === cleanPart(queueState.ownerId)
|
|
21
|
+
&& normalizeRevision(claim.queueRevision) === normalizeRevision(queueState.queueRevision));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function mergeTombstones(existing, additions, limit) {
|
|
25
|
+
var result = Object.assign({}, existing && typeof existing === 'object' ? existing : {});
|
|
26
|
+
var now = new Date().toISOString();
|
|
27
|
+
(Array.isArray(additions) ? additions : []).forEach(function (id) {
|
|
28
|
+
var cleanId = cleanPart(id);
|
|
29
|
+
if (cleanId) result[cleanId] = now;
|
|
30
|
+
});
|
|
31
|
+
var max = Number.isFinite(Number(limit)) ? Math.max(1, Number(limit)) : 100;
|
|
32
|
+
return Object.fromEntries(Object.entries(result)
|
|
33
|
+
.sort(function (left, right) { return String(right[1]).localeCompare(String(left[1])); })
|
|
34
|
+
.slice(0, max));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function normalizeMeta(value) {
|
|
38
|
+
var revision = normalizeRevision(value && value.revision);
|
|
39
|
+
var hasDurableRevision = Boolean(value
|
|
40
|
+
&& Object.prototype.hasOwnProperty.call(value, 'durableRevision'));
|
|
41
|
+
var durableRevision = hasDurableRevision
|
|
42
|
+
? Math.min(normalizeRevision(value.durableRevision), revision)
|
|
43
|
+
: revision;
|
|
44
|
+
var pendingValue = value && value.pendingCommit;
|
|
45
|
+
var pendingRevision = normalizeRevision(pendingValue && pendingValue.revision);
|
|
46
|
+
var pendingCommit = pendingValue && typeof pendingValue === 'object'
|
|
47
|
+
&& pendingRevision > durableRevision
|
|
48
|
+
&& pendingRevision <= revision
|
|
49
|
+
? {
|
|
50
|
+
revision: pendingRevision,
|
|
51
|
+
writerId: cleanPart(pendingValue.writerId),
|
|
52
|
+
startedAt: cleanPart(pendingValue.startedAt)
|
|
53
|
+
}
|
|
54
|
+
: null;
|
|
55
|
+
return {
|
|
56
|
+
revision: revision,
|
|
57
|
+
durableRevision: durableRevision,
|
|
58
|
+
pendingCommit: pendingCommit,
|
|
59
|
+
queueRevision: normalizeRevision(value && value.queueRevision),
|
|
60
|
+
writerId: cleanPart(value && value.writerId),
|
|
61
|
+
updatedAt: cleanPart(value && value.updatedAt),
|
|
62
|
+
queueClaims: normalizeQueueClaims(value && value.queueClaims),
|
|
63
|
+
queueTombstones: normalizeTombstoneMap(value && value.queueTombstones),
|
|
64
|
+
sessionTombstones: normalizeTombstoneMap(value && value.sessionTombstones)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeQueueMutations(options) {
|
|
69
|
+
var values = [];
|
|
70
|
+
if (options && options.queueMutation) values.push(options.queueMutation);
|
|
71
|
+
if (options && Array.isArray(options.queueMutations)) values.push.apply(values, options.queueMutations);
|
|
72
|
+
return values.filter(function (value) {
|
|
73
|
+
return value && typeof value === 'object' && cleanPart(value.type);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function applyQueueMutations(meta, mutations, writerId) {
|
|
78
|
+
var next = normalizeMeta(meta);
|
|
79
|
+
for (var mutation of mutations) {
|
|
80
|
+
var type = cleanPart(mutation.type);
|
|
81
|
+
var itemId = cleanPart(mutation.itemId);
|
|
82
|
+
var sessionId = cleanPart(mutation.sessionId);
|
|
83
|
+
if (!itemId && type !== 'transition') {
|
|
84
|
+
return { ok: false, reason: 'invalid_queue_mutation' };
|
|
85
|
+
}
|
|
86
|
+
if (type === 'remove' || type === 'release' || type === 'restore') {
|
|
87
|
+
var claimed = next.queueClaims[itemId];
|
|
88
|
+
var mutationToken = cleanPart(mutation.claimToken);
|
|
89
|
+
if (claimed && !isExpiredClaim(claimed)
|
|
90
|
+
&& (claimed.ownerId !== writerId
|
|
91
|
+
|| !mutationToken
|
|
92
|
+
|| claimed.claimToken !== mutationToken)) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
reason: 'queue_claim_conflict',
|
|
96
|
+
ownerId: claimed.ownerId,
|
|
97
|
+
itemId: itemId
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (type === 'claim') {
|
|
102
|
+
var existing = next.queueClaims[itemId];
|
|
103
|
+
if (existing && existing.ownerId !== writerId && !isExpiredClaim(existing)) {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
reason: 'queue_claim_conflict',
|
|
107
|
+
ownerId: existing.ownerId,
|
|
108
|
+
itemId: itemId
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
next.queueRevision += 1;
|
|
112
|
+
next.queueClaims[itemId] = {
|
|
113
|
+
...createQueueClaim({
|
|
114
|
+
itemId: itemId,
|
|
115
|
+
ownerId: writerId,
|
|
116
|
+
queueRevision: next.queueRevision
|
|
117
|
+
}),
|
|
118
|
+
sessionId: sessionId,
|
|
119
|
+
claimToken: cleanPart(mutation.claimToken)
|
|
120
|
+
};
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (type === 'remove') {
|
|
124
|
+
next.queueRevision += 1;
|
|
125
|
+
next.queueTombstones = mergeTombstones(next.queueTombstones, [itemId], 500);
|
|
126
|
+
delete next.queueClaims[itemId];
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (type === 'release') {
|
|
130
|
+
next.queueRevision += 1;
|
|
131
|
+
delete next.queueClaims[itemId];
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (type === 'restore') {
|
|
135
|
+
next.queueRevision += 1;
|
|
136
|
+
delete next.queueClaims[itemId];
|
|
137
|
+
delete next.queueTombstones[itemId];
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (type === 'enqueue' || type === 'transition') {
|
|
141
|
+
next.queueRevision += 1;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
return { ok: false, reason: 'invalid_queue_mutation' };
|
|
145
|
+
}
|
|
146
|
+
return { ok: true, meta: next };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeQueueClaims(value) {
|
|
150
|
+
var source = value && typeof value === 'object' ? value : {};
|
|
151
|
+
var result = {};
|
|
152
|
+
Object.entries(source).forEach(function (entry) {
|
|
153
|
+
var itemId = cleanPart(entry[0]);
|
|
154
|
+
var claim = entry[1] && typeof entry[1] === 'object' ? entry[1] : {};
|
|
155
|
+
var ownerId = cleanPart(claim.ownerId);
|
|
156
|
+
if (!itemId || !ownerId) return;
|
|
157
|
+
result[itemId] = {
|
|
158
|
+
itemId: itemId,
|
|
159
|
+
ownerId: ownerId,
|
|
160
|
+
sessionId: cleanPart(claim.sessionId),
|
|
161
|
+
claimToken: cleanPart(claim.claimToken),
|
|
162
|
+
queueRevision: normalizeRevision(claim.queueRevision),
|
|
163
|
+
claimedAt: cleanPart(claim.claimedAt)
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function normalizeTombstoneMap(value) {
|
|
170
|
+
var source = value && typeof value === 'object' ? value : {};
|
|
171
|
+
return Object.fromEntries(Object.entries(source)
|
|
172
|
+
.map(function (entry) { return [cleanPart(entry[0]), cleanPart(entry[1])]; })
|
|
173
|
+
.filter(function (entry) { return entry[0] && entry[1]; }));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function isExpiredClaim(claim, now) {
|
|
177
|
+
var claimedAt = Date.parse(claim && claim.claimedAt || '');
|
|
178
|
+
return !claimedAt || (Number(now || Date.now()) - claimedAt) > 60 * 1000;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function normalizeRevision(value) {
|
|
182
|
+
var number = Number(value);
|
|
183
|
+
return Number.isSafeInteger(number) && number >= 0 ? number : 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function cleanPart(value) {
|
|
187
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return Object.freeze({
|
|
191
|
+
applyQueueMutations: applyQueueMutations,
|
|
192
|
+
cleanPart: cleanPart,
|
|
193
|
+
createQueueClaim: createQueueClaim,
|
|
194
|
+
isClaimCurrent: isClaimCurrent,
|
|
195
|
+
mergeTombstones: mergeTombstones,
|
|
196
|
+
normalizeMeta: normalizeMeta,
|
|
197
|
+
normalizeQueueMutations: normalizeQueueMutations,
|
|
198
|
+
normalizeRevision: normalizeRevision
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory();
|
|
4
|
+
} else {
|
|
5
|
+
root.CodexOverleafModuleRegistry.define('ScopedPersistenceReceipt', [], factory);
|
|
6
|
+
}
|
|
7
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
function createFenceMeta(meta, currentMeta, writerId) {
|
|
11
|
+
var startedAt = new Date().toISOString();
|
|
12
|
+
var revision = currentMeta.revision + 1;
|
|
13
|
+
return {
|
|
14
|
+
...meta,
|
|
15
|
+
revision: revision,
|
|
16
|
+
durableRevision: currentMeta.durableRevision,
|
|
17
|
+
pendingCommit: { revision: revision, writerId: writerId, startedAt: startedAt },
|
|
18
|
+
writerId: writerId,
|
|
19
|
+
updatedAt: startedAt
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createCommittedMeta(fenceMeta) {
|
|
24
|
+
return {
|
|
25
|
+
...fenceMeta,
|
|
26
|
+
durableRevision: fenceMeta.revision,
|
|
27
|
+
pendingCommit: null,
|
|
28
|
+
updatedAt: new Date().toISOString()
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function createRecoveredMeta(currentMeta, fenceMeta, writerId) {
|
|
33
|
+
var revision = fenceMeta.revision + 1;
|
|
34
|
+
return {
|
|
35
|
+
...currentMeta,
|
|
36
|
+
revision: revision,
|
|
37
|
+
durableRevision: revision,
|
|
38
|
+
pendingCommit: null,
|
|
39
|
+
queueRevision: fenceMeta.queueRevision + 1,
|
|
40
|
+
writerId: writerId,
|
|
41
|
+
updatedAt: new Date().toISOString()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function decorateReceiptError(error, revision) {
|
|
46
|
+
if (error && typeof error === 'object') {
|
|
47
|
+
error.code = error.code || 'persistence_receipt_write_failed';
|
|
48
|
+
error.persistenceActionCommitted = true;
|
|
49
|
+
error.persistenceRevision = revision;
|
|
50
|
+
}
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return Object.freeze({
|
|
55
|
+
createFenceMeta: createFenceMeta,
|
|
56
|
+
createCommittedMeta: createCommittedMeta,
|
|
57
|
+
createRecoveredMeta: createRecoveredMeta,
|
|
58
|
+
decorateReceiptError: decorateReceiptError
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
if (typeof module === 'object' && module.exports) {
|
|
3
|
+
module.exports = factory(
|
|
4
|
+
require('./scopedPersistenceQueuePolicy'),
|
|
5
|
+
require('./scopedPersistenceBrowserAdapter'),
|
|
6
|
+
require('./scopedPersistenceReceipt')
|
|
7
|
+
);
|
|
8
|
+
} else {
|
|
9
|
+
root.CodexOverleafModuleRegistry.define('ScopedPersistenceTransaction', [
|
|
10
|
+
'ScopedPersistenceQueuePolicy',
|
|
11
|
+
'ScopedPersistenceBrowserAdapter',
|
|
12
|
+
'ScopedPersistenceReceipt'
|
|
13
|
+
], factory);
|
|
14
|
+
}
|
|
15
|
+
})(typeof globalThis !== 'undefined' ? globalThis : this, function (QueuePolicy, BrowserAdapter, Receipt) {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
var applyQueueMutations = QueuePolicy.applyQueueMutations;
|
|
19
|
+
var cleanPart = QueuePolicy.cleanPart;
|
|
20
|
+
var createQueueClaim = QueuePolicy.createQueueClaim;
|
|
21
|
+
var isClaimCurrent = QueuePolicy.isClaimCurrent;
|
|
22
|
+
var mergeTombstones = QueuePolicy.mergeTombstones;
|
|
23
|
+
var normalizeMeta = QueuePolicy.normalizeMeta;
|
|
24
|
+
var normalizeQueueMutations = QueuePolicy.normalizeQueueMutations;
|
|
25
|
+
var normalizeRevision = QueuePolicy.normalizeRevision;
|
|
26
|
+
var createBrowserAdapter = BrowserAdapter.createBrowserAdapter;
|
|
27
|
+
var normalizeScope = BrowserAdapter.normalizeScope;
|
|
28
|
+
var scopeKey = BrowserAdapter.scopeKey;
|
|
29
|
+
|
|
30
|
+
function sameScope(left, right) {
|
|
31
|
+
return Boolean(left && right
|
|
32
|
+
&& left.accountScopeId === right.accountScopeId
|
|
33
|
+
&& left.projectId === right.projectId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function createCoordinator(options) {
|
|
38
|
+
options = options || {};
|
|
39
|
+
var adapter = options.adapter || createBrowserAdapter(options);
|
|
40
|
+
var writerId = cleanPart(options.writerId) || randomId();
|
|
41
|
+
var generation = 0;
|
|
42
|
+
var active = null;
|
|
43
|
+
|
|
44
|
+
async function beginHydration(inputScope) {
|
|
45
|
+
var scope = normalizeScope(inputScope);
|
|
46
|
+
generation += 1;
|
|
47
|
+
var tokenGeneration = generation;
|
|
48
|
+
active = tokenFromMeta(scope, tokenGeneration, false, {});
|
|
49
|
+
var meta = await strictRead(adapter, scope);
|
|
50
|
+
var token = tokenFromMeta(scope, tokenGeneration, false, meta);
|
|
51
|
+
if (active && active.generation === tokenGeneration && sameScope(active, token)) active = token;
|
|
52
|
+
return token;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function captureCommitView(inputScope, captureOptions) {
|
|
56
|
+
var scope = normalizeScope(inputScope);
|
|
57
|
+
var detached = Boolean(captureOptions && captureOptions.detached);
|
|
58
|
+
if (!detached && active && active.projectId === scope.projectId) {
|
|
59
|
+
scope = normalizeScope({
|
|
60
|
+
accountScopeId: active.accountScopeId,
|
|
61
|
+
projectId: scope.projectId
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (!detached && active && sameScope(active, scope)) {
|
|
65
|
+
return tokenFromMeta(scope, active.generation, false, active);
|
|
66
|
+
}
|
|
67
|
+
var meta = await strictRead(adapter, scope);
|
|
68
|
+
return tokenFromMeta(scope, detached ? 0 : generation, detached, meta);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isCurrent(token) {
|
|
72
|
+
return Boolean(token && active && token.detached !== true
|
|
73
|
+
&& token.generation === active.generation && sameScope(token, active));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function commit(token, action, commitOptions) {
|
|
77
|
+
if (!token || typeof action !== 'function' || !token.projectId) {
|
|
78
|
+
return { ok: false, reason: 'invalid_commit' };
|
|
79
|
+
}
|
|
80
|
+
var scope = normalizeScope(token);
|
|
81
|
+
if (!token.detached && !isCurrent(token)) return { ok: false, reason: 'stale_view' };
|
|
82
|
+
return adapter.withLock(scope, async function () {
|
|
83
|
+
if (!token.detached && !isCurrent(token)) return { ok: false, reason: 'stale_view' };
|
|
84
|
+
var currentMeta = await strictRead(adapter, scope);
|
|
85
|
+
if (!token.detached && !isCurrent(token)) return { ok: false, reason: 'stale_view' };
|
|
86
|
+
var conflict = currentMeta.revision !== normalizeRevision(token.revision);
|
|
87
|
+
if (conflict && !(commitOptions && commitOptions.rebase === true)) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: 'revision_conflict',
|
|
91
|
+
expectedRevision: token.revision,
|
|
92
|
+
actualRevision: currentMeta.revision
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
var queueMutations = normalizeQueueMutations(commitOptions);
|
|
96
|
+
if (queueMutations.length
|
|
97
|
+
&& currentMeta.queueRevision !== normalizeRevision(token.queueRevision)) {
|
|
98
|
+
if (!token.detached && isCurrent(token)) {
|
|
99
|
+
active = tokenFromMeta(scope, token.generation, false, currentMeta);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
reason: 'queue_revision_conflict',
|
|
104
|
+
expectedRevision: token.queueRevision,
|
|
105
|
+
actualRevision: currentMeta.queueRevision,
|
|
106
|
+
actualStateRevision: currentMeta.revision
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
var projected = applyQueueMutations(currentMeta, queueMutations, writerId);
|
|
110
|
+
if (!projected.ok) return projected;
|
|
111
|
+
var nextMeta = Receipt.createFenceMeta({
|
|
112
|
+
...projected.meta,
|
|
113
|
+
sessionTombstones: mergeTombstones(
|
|
114
|
+
projected.meta.sessionTombstones,
|
|
115
|
+
commitOptions && commitOptions.sessionTombstones,
|
|
116
|
+
200
|
|
117
|
+
)
|
|
118
|
+
}, currentMeta, writerId);
|
|
119
|
+
await adapter.write(scope, nextMeta);
|
|
120
|
+
if (!token.detached && isCurrent(token)) {
|
|
121
|
+
active = tokenFromMeta(scope, token.generation, false, nextMeta);
|
|
122
|
+
}
|
|
123
|
+
var value;
|
|
124
|
+
try {
|
|
125
|
+
value = await action({
|
|
126
|
+
scope: scope,
|
|
127
|
+
baseRevision: currentMeta.revision,
|
|
128
|
+
durableRevision: currentMeta.durableRevision,
|
|
129
|
+
conflict: conflict,
|
|
130
|
+
previousCommitIncomplete: Boolean(currentMeta.pendingCommit),
|
|
131
|
+
writerId: writerId,
|
|
132
|
+
currentMeta: currentMeta,
|
|
133
|
+
nextMeta: nextMeta
|
|
134
|
+
});
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (queueMutations.some(function (mutation) {
|
|
137
|
+
return ['remove', 'release', 'restore'].includes(cleanPart(mutation.type));
|
|
138
|
+
})) {
|
|
139
|
+
var recoveredMeta = Receipt.createRecoveredMeta(currentMeta, nextMeta, writerId);
|
|
140
|
+
try {
|
|
141
|
+
await adapter.write(scope, recoveredMeta);
|
|
142
|
+
if (!token.detached && isCurrent(token)) {
|
|
143
|
+
active = tokenFromMeta(scope, token.generation, false, recoveredMeta);
|
|
144
|
+
}
|
|
145
|
+
} catch (recoveryError) {
|
|
146
|
+
if (error && typeof error === 'object') {
|
|
147
|
+
error.persistenceRecoveryError = recoveryError.message || String(recoveryError);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
var committedMeta = Receipt.createCommittedMeta(nextMeta);
|
|
154
|
+
try {
|
|
155
|
+
await adapter.write(scope, committedMeta);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
throw Receipt.decorateReceiptError(error, nextMeta.revision);
|
|
158
|
+
}
|
|
159
|
+
if (!token.detached && isCurrent(token)) {
|
|
160
|
+
active = tokenFromMeta(scope, token.generation, false, committedMeta);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
ok: true,
|
|
164
|
+
value: value,
|
|
165
|
+
revision: committedMeta.revision,
|
|
166
|
+
durableRevision: committedMeta.durableRevision,
|
|
167
|
+
queueRevision: committedMeta.queueRevision,
|
|
168
|
+
queueClaims: committedMeta.queueClaims,
|
|
169
|
+
rebased: conflict,
|
|
170
|
+
recoveredIncompleteCommit: Boolean(currentMeta.pendingCommit),
|
|
171
|
+
superseded: !token.detached && !isCurrent(token)
|
|
172
|
+
};
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return Object.freeze({
|
|
177
|
+
beginHydration: beginHydration,
|
|
178
|
+
captureCommitView: captureCommitView,
|
|
179
|
+
commit: commit,
|
|
180
|
+
getActiveView: function () {
|
|
181
|
+
return active ? tokenFromMeta(active, active.generation, false, active) : null;
|
|
182
|
+
},
|
|
183
|
+
getWriterId: function () { return writerId; },
|
|
184
|
+
invalidate: function () {
|
|
185
|
+
generation += 1;
|
|
186
|
+
active = null;
|
|
187
|
+
},
|
|
188
|
+
isCurrent: isCurrent
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function makeToken(
|
|
193
|
+
scope,
|
|
194
|
+
tokenGeneration,
|
|
195
|
+
revision,
|
|
196
|
+
detached,
|
|
197
|
+
queueRevision,
|
|
198
|
+
durableRevision,
|
|
199
|
+
pendingCommit
|
|
200
|
+
) {
|
|
201
|
+
return Object.freeze({
|
|
202
|
+
accountScopeId: scope.accountScopeId,
|
|
203
|
+
projectId: scope.projectId,
|
|
204
|
+
generation: normalizeRevision(tokenGeneration),
|
|
205
|
+
revision: normalizeRevision(revision),
|
|
206
|
+
durableRevision: normalizeRevision(durableRevision),
|
|
207
|
+
pendingCommit: pendingCommit && typeof pendingCommit === 'object'
|
|
208
|
+
? Object.freeze({ ...pendingCommit })
|
|
209
|
+
: null,
|
|
210
|
+
queueRevision: normalizeRevision(queueRevision),
|
|
211
|
+
detached: detached === true
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function tokenFromMeta(scope, tokenGeneration, detached, meta) {
|
|
216
|
+
return makeToken(scope, tokenGeneration, meta.revision, detached, meta.queueRevision,
|
|
217
|
+
meta.durableRevision, meta.pendingCommit);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function strictRead(adapter, scope) {
|
|
221
|
+
return normalizeMeta(await adapter.read(scope));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function randomId() {
|
|
225
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
226
|
+
return crypto.randomUUID();
|
|
227
|
+
}
|
|
228
|
+
return 'writer-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return Object.freeze({
|
|
232
|
+
createBrowserAdapter: createBrowserAdapter,
|
|
233
|
+
createCoordinator: createCoordinator,
|
|
234
|
+
createQueueClaim: createQueueClaim,
|
|
235
|
+
isClaimCurrent: isClaimCurrent,
|
|
236
|
+
mergeTombstones: mergeTombstones,
|
|
237
|
+
normalizeScope: normalizeScope,
|
|
238
|
+
sameScope: sameScope,
|
|
239
|
+
scopeKey: scopeKey
|
|
240
|
+
});
|
|
241
|
+
});
|