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.
Files changed (72) hide show
  1. package/README.md +25 -23
  2. package/extension/bootstrap/manifest.template.json +1 -1
  3. package/extension/runtime-manifest.json +5 -71
  4. package/extension/src/background.js +53 -69
  5. package/extension/src/backgroundUpdateCoordinator.js +128 -66
  6. package/extension/src/content/applyResultFormatters.js +2 -3
  7. package/extension/src/content/contentRuntime.js +650 -1449
  8. package/extension/src/content/contextTray.js +4 -6
  9. package/extension/src/content/diagnosticsController.js +1 -2
  10. package/extension/src/content/diffReviewPanel.js +14 -13
  11. package/extension/src/content/generated/content.bundle.js +41370 -0
  12. package/extension/src/content/generated/content.bundle.meta.json +102 -0
  13. package/extension/src/content/legacyGlobalRegistry.js +129 -0
  14. package/extension/src/content/markdownText.js +5 -7
  15. package/extension/src/content/modelPicker.js +1 -1
  16. package/extension/src/content/modelPickerSupport.js +10 -5
  17. package/extension/src/content/moduleRegistryKernel.js +125 -0
  18. package/extension/src/content/nativeCompatibilityController.js +313 -0
  19. package/extension/src/content/otWarmMirror.js +7 -5
  20. package/extension/src/content/pageBridgeClient.js +224 -0
  21. package/extension/src/content/panelMaintenance.js +8 -3
  22. package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
  23. package/extension/src/content/projectProviderSelection.js +4 -12
  24. package/extension/src/content/projectSessionCleanup.js +3 -3
  25. package/extension/src/content/providerSettingsCoordinator.js +59 -26
  26. package/extension/src/content/providerSettingsDialog.js +30 -18
  27. package/extension/src/content/recentProjects.js +15 -23
  28. package/extension/src/content/runGuidanceController.js +1 -1
  29. package/extension/src/content/runQueueScheduler.js +129 -29
  30. package/extension/src/content/runSettlementPersistence.js +51 -0
  31. package/extension/src/content/runTimelineView.js +11 -8
  32. package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
  33. package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
  34. package/extension/src/content/scopedPersistencePanelState.js +111 -0
  35. package/extension/src/content/sessionManager.js +15 -12
  36. package/extension/src/content/sessionPersistence.js +67 -8
  37. package/extension/src/content/updateNotice.js +10 -4
  38. package/extension/src/content/writebackOrchestrator.js +91 -23
  39. package/extension/src/contentScript.js +5 -1
  40. package/extension/src/page/trackedChangesLifecycle.js +6 -1
  41. package/extension/src/pageBridge.js +47 -98
  42. package/extension/src/shared/agentTranscript.js +8 -5
  43. package/extension/src/shared/compatibility.js +1 -1
  44. package/extension/src/shared/governanceRules.js +1 -1
  45. package/extension/src/shared/managedUpdateProjection.js +337 -0
  46. package/extension/src/shared/nativeRequestIdentity.js +60 -0
  47. package/extension/src/shared/otText.js +0 -19
  48. package/extension/src/shared/pageRpcContract.js +136 -0
  49. package/extension/src/shared/runExecutionSnapshot.js +177 -0
  50. package/extension/src/shared/runExecutionSnapshotCodec.js +168 -0
  51. package/extension/src/shared/runInputQueue.js +81 -34
  52. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
  53. package/extension/src/shared/scopedPersistenceQueuePolicy.js +181 -0
  54. package/extension/src/shared/scopedPersistenceTransaction.js +238 -0
  55. package/extension/src/shared/sessionState.js +64 -48
  56. package/extension/src/shared/settlementFacts.js +351 -0
  57. package/extension/src/shared/storageDb.js +19 -5
  58. package/extension/src/shared/storageMigration.js +275 -61
  59. package/extension/src/shared/storageSessionClaims.js +60 -0
  60. package/extension/src/shared/updateConsent.js +4 -0
  61. package/extension/src/shared/updateRevocationIntent.js +109 -0
  62. package/extension/src/shared/writebackEvidenceProjection.js +150 -0
  63. package/extension/src/shared/writebackOperationEvidence.js +143 -0
  64. package/extension/src/shared/writebackSettlement.js +705 -0
  65. package/native-host/src/codexRuntimeIdentity.js +1 -1
  66. package/native-host/src/codexSessionRunner.js +7 -13
  67. package/native-host/src/index.js +3 -69
  68. package/native-host/src/managedInstall.js +50 -8
  69. package/native-host/src/mirrorWorkspace.js +15 -8
  70. package/native-host/src/nativeTransportEnvelope.js +148 -0
  71. package/native-host/src/taskRunnerRuntime.js +3 -3
  72. package/package.json +13 -5
@@ -1,15 +1,16 @@
1
1
  (function initCodexOverleafRunInputQueue(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
- module.exports = factory();
3
+ module.exports = factory(require('./runExecutionSnapshot'));
4
4
  } else {
5
- root.CodexOverleafRunInputQueue = factory();
5
+ root.CodexOverleafModuleRegistry.define('RunInputQueue', ['RunExecutionSnapshot'], factory);
6
6
  }
7
- })(typeof window !== 'undefined' ? window : globalThis, function runInputQueueFactory() {
7
+ })(typeof window !== 'undefined' ? window : globalThis, function runInputQueueFactory(RunExecutionSnapshot) {
8
8
  'use strict';
9
9
 
10
10
  const MAX_ITEMS = 20;
11
11
  const MAX_TEXT_CHARS = 12000;
12
12
  const MAX_QUEUE_BYTES = 128 * 1024;
13
+ const MAX_PERSISTED_QUEUE_BYTES = 512 * 1024;
13
14
  const ACTIVE_STATUSES = new Set(['claimed', 'executing', 'steering']);
14
15
  const VALID_STATUSES = new Set(['queued', 'paused', ...ACTIVE_STATUSES]);
15
16
 
@@ -27,6 +28,7 @@
27
28
  if (options.recoverActive === true && ACTIVE_STATUSES.has(status)) {
28
29
  status = 'paused';
29
30
  }
31
+ const executionSnapshot = normalizeExecutionSnapshot(raw, options.fallbackInput);
30
32
  normalized.push({
31
33
  id,
32
34
  clientUserMessageId: String(raw.clientUserMessageId || id),
@@ -38,7 +40,8 @@
38
40
  linkedRunId: status === 'executing' ? String(raw.linkedRunId || '') : '',
39
41
  claimToken: status === 'claimed' || status === 'executing' ? String(raw.claimToken || '') : '',
40
42
  pauseReason: status === 'paused' ? String(raw.pauseReason || '') : '',
41
- payload: normalizePayload(raw.payload)
43
+ executionSnapshot: RunExecutionSnapshot.cloneSnapshot(executionSnapshot),
44
+ payload: RunExecutionSnapshot.toQueuePayload(executionSnapshot)
42
45
  });
43
46
  if (normalized.length >= MAX_ITEMS) {
44
47
  break;
@@ -63,6 +66,7 @@
63
66
  const nowFactory = deps.now || (() => new Date().toISOString());
64
67
  const id = String(input.id || idFactory());
65
68
  const timestamp = String(nowFactory());
69
+ const executionSnapshot = normalizeExecutionSnapshot(input, input.fallbackInput);
66
70
  const item = {
67
71
  id,
68
72
  clientUserMessageId: String(input.clientUserMessageId || id),
@@ -74,10 +78,11 @@
74
78
  linkedRunId: '',
75
79
  claimToken: '',
76
80
  pauseReason: input.status === 'paused' ? String(input.pauseReason || '') : '',
77
- payload: normalizePayload(input.payload)
81
+ executionSnapshot: RunExecutionSnapshot.cloneSnapshot(executionSnapshot),
82
+ payload: RunExecutionSnapshot.toQueuePayload(executionSnapshot)
78
83
  };
79
84
  const next = [...queue, item];
80
- if (estimateBytes(next) > MAX_QUEUE_BYTES) {
85
+ if (estimateLegacyBytes(next) > MAX_QUEUE_BYTES || estimateBytes(next) > MAX_PERSISTED_QUEUE_BYTES) {
81
86
  return failure(queue, 'queue_too_large');
82
87
  }
83
88
  return { ok: true, queue: next, item };
@@ -114,13 +119,37 @@
114
119
  return { ok: false, queue, item: null };
115
120
  }
116
121
  const tokenFactory = deps.randomUUID || (() => 'claim_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8));
117
- const item = {
122
+ let item = {
118
123
  ...queue[index],
119
124
  status: 'claimed',
120
125
  claimToken: String(tokenFactory()),
121
126
  updatedAt: new Date().toISOString(),
122
127
  pauseReason: ''
123
128
  };
129
+ try {
130
+ const captured = RunExecutionSnapshot.normalizeSnapshot(
131
+ item.executionSnapshot || item.payload?.executionSnapshot || item.payload,
132
+ { source: item.executionSnapshot?.source || item.payload?.executionSnapshot?.source || 'submitted' }
133
+ );
134
+ const snapshot = deps.resolveExecutionSnapshot instanceof Function
135
+ ? deps.resolveExecutionSnapshot(captured)
136
+ : captured;
137
+ item = {
138
+ ...item,
139
+ executionSnapshot: RunExecutionSnapshot.cloneSnapshot(snapshot),
140
+ payload: RunExecutionSnapshot.toQueuePayload(snapshot)
141
+ };
142
+ } catch (error) {
143
+ return {
144
+ ok: false,
145
+ queue,
146
+ item: null,
147
+ error: {
148
+ code: error?.code || 'provider_revision_conflict',
149
+ message: error?.message || ''
150
+ }
151
+ };
152
+ }
124
153
  queue[index] = item;
125
154
  return { ok: true, queue, item };
126
155
  }
@@ -160,21 +189,14 @@
160
189
  return normalizeQueue(items).map(item => item.id === id ? updater(item) : item);
161
190
  }
162
191
 
163
- function normalizePayload(payload = {}) {
164
- return {
165
- mode: ['ask', 'confirm', 'auto'].includes(payload.mode) ? payload.mode : 'ask',
166
- providerId: String(payload.providerId || 'builtin').slice(0, 160),
167
- providerRevision: String(payload.providerRevision || '').slice(0, 160),
168
- model: String(payload.model || '').slice(0, 160),
169
- reasoningEffort: String(payload.reasoningEffort || '').slice(0, 32),
170
- speedTier: payload.speedTier === 'fast' ? 'fast' : 'standard',
171
- autoRecompile: payload.autoRecompile !== false,
172
- requireReviewing: payload.requireReviewing !== false,
173
- focusFiles: (Array.isArray(payload.focusFiles) ? payload.focusFiles : [])
174
- .map(path => String(path || '').replace(/\\/g, '/').replace(/^\/+/, '').trim())
175
- .filter(Boolean)
176
- .slice(0, 100)
177
- };
192
+ function normalizeExecutionSnapshot(raw = {}, fallbackInput = {}) {
193
+ return RunExecutionSnapshot.captureRawQueueTuple(raw, fallbackInput);
194
+ }
195
+
196
+ function normalizePayload(payload = {}, fallbackInput = {}) {
197
+ return RunExecutionSnapshot.toQueuePayload(
198
+ RunExecutionSnapshot.captureRawQueueTuple(payload, fallbackInput)
199
+ );
178
200
  }
179
201
 
180
202
  function normalizeTimestamp(value) {
@@ -183,7 +205,10 @@
183
205
 
184
206
  function trimToBudget(items) {
185
207
  const queue = items.slice(-MAX_ITEMS);
186
- while (queue.length && estimateBytes(queue) > MAX_QUEUE_BYTES) {
208
+ while (queue.length && (
209
+ estimateLegacyBytes(queue) > MAX_QUEUE_BYTES
210
+ || estimateBytes(queue) > MAX_PERSISTED_QUEUE_BYTES
211
+ )) {
187
212
  queue.shift();
188
213
  }
189
214
  return queue;
@@ -196,6 +221,29 @@
196
221
  : text.length * 2;
197
222
  }
198
223
 
224
+ function estimateLegacyBytes(items) {
225
+ return estimateBytes((Array.isArray(items) ? items : []).map(item => {
226
+ const legacyItem = { ...(item || {}) };
227
+ delete legacyItem.executionSnapshot;
228
+ return {
229
+ ...legacyItem,
230
+ payload: item?.payload && typeof item.payload === 'object'
231
+ ? {
232
+ mode: item.payload.mode,
233
+ providerId: item.payload.providerId,
234
+ providerRevision: item.payload.providerRevision,
235
+ model: item.payload.model,
236
+ reasoningEffort: item.payload.reasoningEffort,
237
+ speedTier: item.payload.speedTier,
238
+ autoRecompile: item.payload.autoRecompile,
239
+ requireReviewing: item.payload.requireReviewing,
240
+ focusFiles: item.payload.focusFiles
241
+ }
242
+ : {}
243
+ };
244
+ }));
245
+ }
246
+
199
247
  function compactForStorage(items, helpers = {}) {
200
248
  const normalizeField = helpers.normalizeField || (value => String(value || ''));
201
249
  const normalizeDisplay = helpers.normalizeDisplay || normalizeField;
@@ -204,6 +252,11 @@
204
252
  .slice(-MAX_ITEMS)
205
253
  .map(item => {
206
254
  const payload = item?.payload && typeof item.payload === 'object' ? item.payload : {};
255
+ const snapshot = RunExecutionSnapshot.captureRawQueueTuple(item, payload);
256
+ const compactSnapshot = RunExecutionSnapshot.cloneSnapshot({
257
+ ...snapshot,
258
+ focusFiles: normalizePaths(snapshot.focusFiles)
259
+ });
207
260
  return {
208
261
  id: normalizeField(item?.id, 160),
209
262
  clientUserMessageId: normalizeField(item?.clientUserMessageId, 160),
@@ -215,17 +268,8 @@
215
268
  linkedRunId: normalizeField(item?.linkedRunId, 160),
216
269
  claimToken: normalizeField(item?.claimToken, 160),
217
270
  pauseReason: normalizeField(item?.pauseReason, 160),
218
- payload: {
219
- mode: typeof payload.mode === 'string' ? payload.mode : 'ask',
220
- providerId: normalizeField(payload.providerId, 160) || 'builtin',
221
- providerRevision: normalizeField(payload.providerRevision, 160),
222
- model: normalizeField(payload.model, 160),
223
- reasoningEffort: normalizeField(payload.reasoningEffort, 32),
224
- speedTier: payload.speedTier === 'fast' ? 'fast' : 'standard',
225
- autoRecompile: payload.autoRecompile !== false,
226
- requireReviewing: payload.requireReviewing !== false,
227
- focusFiles: normalizePaths(payload.focusFiles)
228
- }
271
+ executionSnapshot: compactSnapshot,
272
+ payload: RunExecutionSnapshot.toQueuePayload(compactSnapshot)
229
273
  };
230
274
  })
231
275
  .filter(item => item.id && item.text);
@@ -238,10 +282,13 @@
238
282
  return {
239
283
  MAX_ITEMS,
240
284
  MAX_QUEUE_BYTES,
285
+ MAX_PERSISTED_QUEUE_BYTES,
241
286
  MAX_TEXT_CHARS,
242
287
  claimNext,
243
288
  compactForStorage,
244
289
  enqueue,
290
+ estimateBytes,
291
+ estimateLegacyBytes,
245
292
  markExecuting,
246
293
  markSteering,
247
294
  normalizeQueue,
@@ -0,0 +1,79 @@
1
+ (function (root, factory) {
2
+ if (typeof module === 'object' && module.exports) {
3
+ module.exports = factory(require('./scopedPersistenceQueuePolicy'));
4
+ } else {
5
+ root.CodexOverleafModuleRegistry.define(
6
+ 'ScopedPersistenceBrowserAdapter',
7
+ ['ScopedPersistenceQueuePolicy'],
8
+ factory
9
+ );
10
+ }
11
+ })(typeof globalThis !== 'undefined' ? globalThis : this, function (QueuePolicy) {
12
+ 'use strict';
13
+
14
+ var META_PREFIX = 'codex-overleaf-scoped-persistence-v1:';
15
+ var fallbackMeta = new Map();
16
+ var cleanPart = QueuePolicy.cleanPart;
17
+ var normalizeMeta = QueuePolicy.normalizeMeta;
18
+
19
+ function normalizeScope(input) {
20
+ return Object.freeze({
21
+ accountScopeId: cleanPart(input && input.accountScopeId),
22
+ projectId: cleanPart(input && input.projectId)
23
+ });
24
+ }
25
+
26
+ function scopeKey(scope) {
27
+ var normalized = normalizeScope(scope);
28
+ if (!normalized.accountScopeId) {
29
+ var error = new Error('A stable account scope is required for durable persistence.');
30
+ error.code = 'account_scope_unavailable';
31
+ throw error;
32
+ }
33
+ return META_PREFIX + encodeURIComponent(normalized.accountScopeId)
34
+ + ':' + encodeURIComponent(normalized.projectId);
35
+ }
36
+
37
+ function createBrowserAdapter(options) {
38
+ options = options || {};
39
+ var chromeApi = options.chromeApi || (typeof chrome !== 'undefined' ? chrome : null);
40
+ var locksApi = Object.prototype.hasOwnProperty.call(options, 'locksApi')
41
+ ? options.locksApi
42
+ : (typeof navigator !== 'undefined' ? navigator.locks : null);
43
+ return {
44
+ async read(scope) {
45
+ var key = scopeKey(scope);
46
+ if (!chromeApi || !chromeApi.storage || !chromeApi.storage.local) {
47
+ return normalizeMeta(fallbackMeta.get(key));
48
+ }
49
+ var stored = await chromeApi.storage.local.get(key);
50
+ return normalizeMeta(stored && stored[key]);
51
+ },
52
+ async write(scope, meta) {
53
+ var key = scopeKey(scope);
54
+ var normalized = normalizeMeta(meta);
55
+ if (!chromeApi || !chromeApi.storage || !chromeApi.storage.local) {
56
+ fallbackMeta.set(key, normalized);
57
+ return normalized;
58
+ }
59
+ await chromeApi.storage.local.set({ [key]: normalized });
60
+ return normalized;
61
+ },
62
+ withLock(scope, work) {
63
+ var key = scopeKey(scope);
64
+ if (locksApi && typeof locksApi.request === 'function') {
65
+ return locksApi.request(key, { mode: 'exclusive' }, work);
66
+ }
67
+ var error = new Error('A cross-realm browser lock is required for durable persistence.');
68
+ error.code = 'storage_lock_unavailable';
69
+ return Promise.reject(error);
70
+ }
71
+ };
72
+ }
73
+
74
+ return Object.freeze({
75
+ createBrowserAdapter: createBrowserAdapter,
76
+ normalizeScope: normalizeScope,
77
+ scopeKey: scopeKey
78
+ });
79
+ });
@@ -0,0 +1,181 @@
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
+ return {
39
+ revision: normalizeRevision(value && value.revision),
40
+ queueRevision: normalizeRevision(value && value.queueRevision),
41
+ writerId: cleanPart(value && value.writerId),
42
+ updatedAt: cleanPart(value && value.updatedAt),
43
+ queueClaims: normalizeQueueClaims(value && value.queueClaims),
44
+ queueTombstones: normalizeTombstoneMap(value && value.queueTombstones),
45
+ sessionTombstones: normalizeTombstoneMap(value && value.sessionTombstones)
46
+ };
47
+ }
48
+
49
+ function normalizeQueueMutations(options) {
50
+ var values = [];
51
+ if (options && options.queueMutation) values.push(options.queueMutation);
52
+ if (options && Array.isArray(options.queueMutations)) values.push.apply(values, options.queueMutations);
53
+ return values.filter(function (value) {
54
+ return value && typeof value === 'object' && cleanPart(value.type);
55
+ });
56
+ }
57
+
58
+ function applyQueueMutations(meta, mutations, writerId) {
59
+ var next = normalizeMeta(meta);
60
+ for (var mutation of mutations) {
61
+ var type = cleanPart(mutation.type);
62
+ var itemId = cleanPart(mutation.itemId);
63
+ var sessionId = cleanPart(mutation.sessionId);
64
+ if (!itemId && type !== 'transition') {
65
+ return { ok: false, reason: 'invalid_queue_mutation' };
66
+ }
67
+ if (type === 'remove' || type === 'release' || type === 'restore') {
68
+ var claimed = next.queueClaims[itemId];
69
+ var mutationToken = cleanPart(mutation.claimToken);
70
+ if (claimed && !isExpiredClaim(claimed)
71
+ && (claimed.ownerId !== writerId
72
+ || !mutationToken
73
+ || claimed.claimToken !== mutationToken)) {
74
+ return {
75
+ ok: false,
76
+ reason: 'queue_claim_conflict',
77
+ ownerId: claimed.ownerId,
78
+ itemId: itemId
79
+ };
80
+ }
81
+ }
82
+ if (type === 'claim') {
83
+ var existing = next.queueClaims[itemId];
84
+ if (existing && existing.ownerId !== writerId && !isExpiredClaim(existing)) {
85
+ return {
86
+ ok: false,
87
+ reason: 'queue_claim_conflict',
88
+ ownerId: existing.ownerId,
89
+ itemId: itemId
90
+ };
91
+ }
92
+ next.queueRevision += 1;
93
+ next.queueClaims[itemId] = {
94
+ ...createQueueClaim({
95
+ itemId: itemId,
96
+ ownerId: writerId,
97
+ queueRevision: next.queueRevision
98
+ }),
99
+ sessionId: sessionId,
100
+ claimToken: cleanPart(mutation.claimToken)
101
+ };
102
+ continue;
103
+ }
104
+ if (type === 'remove') {
105
+ next.queueRevision += 1;
106
+ next.queueTombstones = mergeTombstones(next.queueTombstones, [itemId], 500);
107
+ delete next.queueClaims[itemId];
108
+ continue;
109
+ }
110
+ if (type === 'release') {
111
+ next.queueRevision += 1;
112
+ delete next.queueClaims[itemId];
113
+ continue;
114
+ }
115
+ if (type === 'restore') {
116
+ next.queueRevision += 1;
117
+ delete next.queueClaims[itemId];
118
+ delete next.queueTombstones[itemId];
119
+ continue;
120
+ }
121
+ if (type === 'enqueue' || type === 'transition') {
122
+ next.queueRevision += 1;
123
+ continue;
124
+ }
125
+ return { ok: false, reason: 'invalid_queue_mutation' };
126
+ }
127
+ return { ok: true, meta: next };
128
+ }
129
+
130
+ function normalizeQueueClaims(value) {
131
+ var source = value && typeof value === 'object' ? value : {};
132
+ var result = {};
133
+ Object.entries(source).forEach(function (entry) {
134
+ var itemId = cleanPart(entry[0]);
135
+ var claim = entry[1] && typeof entry[1] === 'object' ? entry[1] : {};
136
+ var ownerId = cleanPart(claim.ownerId);
137
+ if (!itemId || !ownerId) return;
138
+ result[itemId] = {
139
+ itemId: itemId,
140
+ ownerId: ownerId,
141
+ sessionId: cleanPart(claim.sessionId),
142
+ claimToken: cleanPart(claim.claimToken),
143
+ queueRevision: normalizeRevision(claim.queueRevision),
144
+ claimedAt: cleanPart(claim.claimedAt)
145
+ };
146
+ });
147
+ return result;
148
+ }
149
+
150
+ function normalizeTombstoneMap(value) {
151
+ var source = value && typeof value === 'object' ? value : {};
152
+ return Object.fromEntries(Object.entries(source)
153
+ .map(function (entry) { return [cleanPart(entry[0]), cleanPart(entry[1])]; })
154
+ .filter(function (entry) { return entry[0] && entry[1]; }));
155
+ }
156
+
157
+ function isExpiredClaim(claim, now) {
158
+ var claimedAt = Date.parse(claim && claim.claimedAt || '');
159
+ return !claimedAt || (Number(now || Date.now()) - claimedAt) > 60 * 1000;
160
+ }
161
+
162
+ function normalizeRevision(value) {
163
+ var number = Number(value);
164
+ return Number.isSafeInteger(number) && number >= 0 ? number : 0;
165
+ }
166
+
167
+ function cleanPart(value) {
168
+ return typeof value === 'string' ? value.trim() : '';
169
+ }
170
+
171
+ return Object.freeze({
172
+ applyQueueMutations: applyQueueMutations,
173
+ cleanPart: cleanPart,
174
+ createQueueClaim: createQueueClaim,
175
+ isClaimCurrent: isClaimCurrent,
176
+ mergeTombstones: mergeTombstones,
177
+ normalizeMeta: normalizeMeta,
178
+ normalizeQueueMutations: normalizeQueueMutations,
179
+ normalizeRevision: normalizeRevision
180
+ });
181
+ });