@abloatai/humans 0.56.0 → 0.58.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/dist/Ablo.d.ts +8 -18
- package/dist/client.d.ts +6 -19
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/local/Database.d.ts +1 -1
- package/dist/local/Model.js +1 -1
- package/dist/local/SyncClient.d.ts +3 -34
- package/dist/local/SyncClient.js +6 -16
- package/dist/local/client/createInternalComponents.d.ts +1 -1
- package/dist/local/client/createInternalComponents.js +1 -1
- package/dist/local/client/{createModelProxy.d.ts → createModelOperations.d.ts} +22 -9
- package/dist/local/client/{createModelProxy.js → createModelOperations.js} +341 -335
- package/dist/local/client/options.d.ts +2 -2
- package/dist/local/client/reactiveEngine.js +38 -45
- package/dist/local/client/resourceTypes.d.ts +3 -3
- package/dist/local/client/resourceTypes.js +1 -1
- package/dist/local/client/wsMutationExecutor.d.ts +4 -4
- package/dist/local/client/wsMutationExecutor.js +4 -5
- package/dist/local/coordination/ClaimLog.d.ts +3 -3
- package/dist/local/coordination/ClaimLog.js +2 -2
- package/dist/local/interfaces/index.d.ts +5 -7
- package/dist/local/mutators/mutateActions.d.ts +3 -2
- package/dist/local/query/types.d.ts +2 -2
- package/dist/local/stores/syncAction.d.ts +1 -1
- package/dist/local/stores/syncAction.js +1 -1
- package/dist/local/sync/ConnectionManager.d.ts +1 -1
- package/dist/local/sync/ConnectionManager.js +1 -1
- package/dist/local/sync/OnDemandLoader.d.ts +1 -1
- package/dist/local/sync/OnDemandLoader.js +1 -1
- package/dist/local/sync/SyncWebSocket.d.ts +3 -3
- package/dist/local/sync/SyncWebSocket.js +2 -2
- package/dist/local/sync/commitFrames.d.ts +1 -1
- package/dist/local/sync/commitFrames.js +1 -1
- package/dist/local/sync/contextOnChange.d.ts +8 -0
- package/dist/local/sync/contextOnChange.js +65 -0
- package/dist/local/sync/createClaimStream.d.ts +1 -1
- package/dist/local/sync/createClaimStream.js +47 -28
- package/dist/local/sync/credentialLifecycle.d.ts +1 -1
- package/dist/local/sync/credentialLifecycle.js +1 -1
- package/dist/local/sync/participants.d.ts +1 -1
- package/dist/local/sync/schemas.d.ts +2 -2
- package/dist/local/sync/schemas.js +1 -1
- package/dist/local/sync/wsFrameHandlers.d.ts +1 -1
- package/dist/local/sync/wsFrameHandlers.js +1 -1
- package/dist/local/syncClientTypes.d.ts +41 -0
- package/dist/local/syncClientTypes.js +11 -0
- package/dist/local/transactions/databaseCommitOutbox.d.ts +2 -2
- package/dist/local/transactions/mutations/MutationQueue.d.ts +5 -18
- package/dist/local/transactions/mutations/MutationQueue.js +57 -137
- package/dist/local/transactions/mutations/batchProcessing.d.ts +2 -13
- package/dist/local/transactions/mutations/batchProcessing.js +0 -20
- package/dist/local/transactions/mutations/commitApi.d.ts +2 -3
- package/dist/local/transactions/mutations/commitApi.js +0 -4
- package/dist/local/transactions/mutations/commitLane.d.ts +6 -8
- package/dist/local/transactions/mutations/commitLane.js +12 -14
- package/dist/local/transactions/mutations/commitPayload.d.ts +2 -4
- package/dist/local/transactions/mutations/commitPayload.js +2 -6
- package/dist/local/transactions/mutations/commitTransport.d.ts +3 -4
- package/dist/local/transactions/mutations/commitTransport.js +2 -5
- package/dist/local/transactions/mutations/durableCommitRestore.d.ts +1 -1
- package/dist/local/transactions/mutations/durableCommitRestore.js +1 -4
- package/dist/local/transactions/mutations/durableWriteStore.d.ts +5 -5
- package/dist/local/transactions/mutations/durableWriteStore.js +3 -3
- package/dist/local/transactions/mutations/failurePolicy.d.ts +2 -2
- package/dist/local/transactions/mutations/mutationInput.d.ts +40 -0
- package/dist/local/transactions/mutations/mutationInput.js +53 -0
- package/dist/local/transactions/mutations/mutationPersistence.d.ts +1 -1
- package/dist/local/transactions/mutations/pendingDrain.d.ts +2 -2
- package/dist/local/transactions/mutations/replayValidation.d.ts +0 -20
- package/dist/local/transactions/mutations/replayValidation.js +1 -3
- package/dist/local/transactions/persistedTransaction.d.ts +0 -2
- package/dist/plugin.d.ts +2 -2
- package/dist/presenceStream.d.ts +1 -1
- package/dist/react/createAbloReact.d.ts +1 -1
- package/dist/react/useAblo.d.ts +2 -2
- package/dist/react/useAblo.js +1 -1
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +2 -2
- package/package.json +2 -2
- package/src/Ablo.ts +9 -27
- package/src/client.ts +6 -22
- package/src/core.ts +1 -1
- package/src/local/Database.ts +1 -2
- package/src/local/Model.ts +1 -1
- package/src/local/SyncClient.ts +17 -72
- package/src/local/client/createInternalComponents.ts +2 -2
- package/src/local/client/{createModelProxy.ts → createModelOperations.ts} +396 -320
- package/src/local/client/options.ts +2 -2
- package/src/local/client/reactiveEngine.ts +67 -55
- package/src/local/client/resourceTypes.ts +4 -4
- package/src/local/client/wsMutationExecutor.ts +4 -9
- package/src/local/coordination/ClaimLog.ts +3 -3
- package/src/local/interfaces/index.ts +5 -7
- package/src/local/mutators/mutateActions.ts +3 -2
- package/src/local/query/types.ts +2 -2
- package/src/local/stores/syncAction.ts +1 -1
- package/src/local/sync/ConnectionManager.ts +1 -1
- package/src/local/sync/OnDemandLoader.ts +1 -1
- package/src/local/sync/SyncWebSocket.ts +3 -3
- package/src/local/sync/commitFrames.ts +1 -2
- package/src/local/sync/contextOnChange.ts +92 -0
- package/src/local/sync/createClaimStream.ts +77 -39
- package/src/local/sync/credentialLifecycle.ts +1 -1
- package/src/local/sync/participants.ts +1 -1
- package/src/local/sync/schemas.ts +1 -1
- package/src/local/sync/wsFrameHandlers.ts +1 -1
- package/src/local/syncClientTypes.ts +59 -0
- package/src/local/transactions/databaseCommitOutbox.ts +2 -2
- package/src/local/transactions/mutations/MutationQueue.ts +69 -183
- package/src/local/transactions/mutations/batchProcessing.ts +2 -38
- package/src/local/transactions/mutations/commitApi.ts +3 -7
- package/src/local/transactions/mutations/commitLane.ts +24 -20
- package/src/local/transactions/mutations/commitPayload.ts +3 -11
- package/src/local/transactions/mutations/commitTransport.ts +3 -7
- package/src/local/transactions/mutations/durableCommitRestore.ts +2 -5
- package/src/local/transactions/mutations/durableWriteStore.ts +5 -5
- package/src/local/transactions/mutations/failurePolicy.ts +2 -2
- package/src/local/transactions/mutations/mutationInput.ts +69 -0
- package/src/local/transactions/mutations/mutationPersistence.ts +1 -1
- package/src/local/transactions/mutations/pendingDrain.ts +2 -2
- package/src/local/transactions/mutations/replayValidation.ts +1 -3
- package/src/local/transactions/persistedTransaction.ts +0 -3
- package/src/plugin.ts +2 -2
- package/src/presenceStream.ts +2 -2
- package/src/react/createAbloReact.ts +1 -1
- package/src/react/useAblo.ts +2 -2
- package/src/surface.ts +3 -3
- package/dist/local/sync/createSnapshot.d.ts +0 -29
- package/dist/local/sync/createSnapshot.js +0 -116
- package/src/local/sync/createSnapshot.ts +0 -160
|
@@ -18,44 +18,39 @@ import type { LocalModel } from '../../localModelContract.js';
|
|
|
18
18
|
import type { MutationPersistencePort } from '../../mutationPersistence.js';
|
|
19
19
|
import { globalRuntime } from '../../context.js';
|
|
20
20
|
import type { RuntimeContext } from '../../RuntimeContext.js';
|
|
21
|
-
import type { MutationOperationType } from '@abloatai/transaction/types';
|
|
22
21
|
import {
|
|
23
|
-
AbloError,
|
|
24
22
|
AbloConnectionError,
|
|
25
23
|
AbloIdempotencyError,
|
|
26
|
-
AbloNotFoundError,
|
|
27
|
-
AbloValidationError,
|
|
28
|
-
errorCodeSpec,
|
|
29
24
|
} from '@abloatai/transaction/errors';
|
|
30
25
|
import {
|
|
31
26
|
LogPosition,
|
|
32
27
|
type LogPositionPort,
|
|
33
28
|
} from '../../logPosition.js';
|
|
34
29
|
import type { WriteOptions } from '../../interfaces/index.js';
|
|
35
|
-
import type {
|
|
30
|
+
import type { ReadDependency } from '@abloatai/transaction/coordination/schema';
|
|
36
31
|
import {
|
|
37
|
-
|
|
32
|
+
type CommitOperationResult,
|
|
38
33
|
type MutationCommitResult,
|
|
39
|
-
} from '@abloatai/transaction/
|
|
34
|
+
} from '@abloatai/transaction/commit';
|
|
40
35
|
import {
|
|
41
|
-
projectCommitPayload,
|
|
42
36
|
computePriorityScore,
|
|
43
37
|
normalizeModelKey,
|
|
44
38
|
// Includes stale guards as well as request identity/audit barriers.
|
|
45
|
-
hasCommitCoalescingBarrier,
|
|
46
|
-
applyWriteOptions,
|
|
47
|
-
asTransportError,
|
|
48
|
-
extractStatusCode,
|
|
49
|
-
TX_TYPE_TO_MUTATION_OP,
|
|
50
39
|
type MutationInput,
|
|
51
40
|
type QueuedMutation,
|
|
52
41
|
type UserContext,
|
|
53
|
-
type WriteOperationFields,
|
|
54
42
|
} from './commitPayload.js';
|
|
43
|
+
import {
|
|
44
|
+
generateTransactionId,
|
|
45
|
+
mergeMutationData,
|
|
46
|
+
createDataFor,
|
|
47
|
+
changesToInput,
|
|
48
|
+
updateDataFor,
|
|
49
|
+
previousDataFor,
|
|
50
|
+
} from './mutationInput.js';
|
|
55
51
|
import { MutationStore } from './MutationStore.js';
|
|
56
52
|
import {
|
|
57
53
|
entityKey,
|
|
58
|
-
mergeUpdateData,
|
|
59
54
|
takeUnsentCreateForModel,
|
|
60
55
|
findCreateBarrierForDelete,
|
|
61
56
|
deferDeleteUntilCreateSettles,
|
|
@@ -66,9 +61,6 @@ import {
|
|
|
66
61
|
deserializePersistedTransaction,
|
|
67
62
|
isNonReplayablePersistedRow,
|
|
68
63
|
pendingMutationRecordId,
|
|
69
|
-
legacyPendingMutationRecordSchema,
|
|
70
|
-
pendingMutationRecordSchema,
|
|
71
|
-
persistedMutationSchema,
|
|
72
64
|
} from './replayValidation.js';
|
|
73
65
|
import {
|
|
74
66
|
deserializeLegacyPendingMutation,
|
|
@@ -80,16 +72,10 @@ import {
|
|
|
80
72
|
} from './mutationPersistence.js';
|
|
81
73
|
import {
|
|
82
74
|
createCommitEnvelopeMember,
|
|
83
|
-
createDurableCommitEnvelope,
|
|
84
|
-
commitEnvelopeRecordId,
|
|
85
|
-
durableCommitEnvelopeSchema,
|
|
86
75
|
type DurableCommitEnvelope,
|
|
87
|
-
type DurableCommitOperation,
|
|
88
|
-
type DurableCommitOperationInput,
|
|
89
76
|
type CommitOutboxScope,
|
|
90
|
-
} from '@abloatai/transaction/
|
|
77
|
+
} from '@abloatai/transaction/commit';
|
|
91
78
|
import type { DurableWriteStore } from './durableWriteStore.js';
|
|
92
|
-
import { stableStringify } from '@abloatai/transaction/utils/json';
|
|
93
79
|
import {
|
|
94
80
|
createLocalMutationPort,
|
|
95
81
|
type LocalMutationPort,
|
|
@@ -101,7 +87,6 @@ import {
|
|
|
101
87
|
removeDurableCommit,
|
|
102
88
|
sealDurableCommit,
|
|
103
89
|
type CommitTransportContext,
|
|
104
|
-
type SealDurableCommitInput,
|
|
105
90
|
} from './commitTransport.js';
|
|
106
91
|
import {
|
|
107
92
|
processCommitLane,
|
|
@@ -122,7 +107,7 @@ import {
|
|
|
122
107
|
import { enqueueTransaction, type QueueCoalescingContext } from './queueCoalescing.js';
|
|
123
108
|
import { processBatch, type BatchProcessingContext } from './batchProcessing.js';
|
|
124
109
|
import { handleFailure, type FailureHandlingContext } from './failureHandling.js';
|
|
125
|
-
import { handleConflict as resolveConflict, isPermanentError as classifyPermanentError, isDefinitiveRejection as classifyDefinitiveRejection, type
|
|
110
|
+
import { handleConflict as resolveConflict, isPermanentError as classifyPermanentError, isDefinitiveRejection as classifyDefinitiveRejection, type ConflictResolutionContext } from './failurePolicy.js';
|
|
126
111
|
import { takeNextExecutionBatch as selectExecutionBatch, takePendingDrainBatch as selectPendingDrainBatch } from './executionSelection.js';
|
|
127
112
|
import { scheduleProcessing as scheduleProcessingExternal, type ProcessingSchedulerContext } from './processingScheduler.js';
|
|
128
113
|
import {
|
|
@@ -245,18 +230,18 @@ export class MutationQueue extends EventEmitter {
|
|
|
245
230
|
private readonly runtime: RuntimeContext;
|
|
246
231
|
/** Durable transaction journal owned by this queue, before commit sealing. */
|
|
247
232
|
private persistence: MutationPersistencePort | null = null;
|
|
248
|
-
private deferredMutations:
|
|
233
|
+
private deferredMutations: {
|
|
249
234
|
type: 'create' | 'update' | 'delete' | 'archive';
|
|
250
235
|
model: LocalModel;
|
|
251
236
|
capturedChanges?: Record<string, unknown>;
|
|
252
237
|
writeOptions?: WriteOptions;
|
|
253
|
-
}
|
|
254
|
-
private pendingPersistenceStages:
|
|
238
|
+
}[] = [];
|
|
239
|
+
private pendingPersistenceStages: {
|
|
255
240
|
transaction: QueuedMutation;
|
|
256
241
|
modelData: Record<string, unknown>;
|
|
257
242
|
resolve: () => void;
|
|
258
243
|
reject: (error: Error) => void;
|
|
259
|
-
}
|
|
244
|
+
}[] = [];
|
|
260
245
|
private persistenceStageScheduled = false;
|
|
261
246
|
private pendingDrainPromise: Promise<void> | null = null;
|
|
262
247
|
|
|
@@ -315,7 +300,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
315
300
|
commitOutbox: this.commitOutbox,
|
|
316
301
|
commitOutboxScope: this.commitOutboxScope,
|
|
317
302
|
mutationExecutor: this.mutationExecutor,
|
|
318
|
-
emitCommitLifecycle: (event, payload) => this.emitCommitLifecycle(event, payload),
|
|
303
|
+
emitCommitLifecycle: (event, payload) => { this.emitCommitLifecycle(event, payload); },
|
|
319
304
|
};
|
|
320
305
|
}
|
|
321
306
|
|
|
@@ -328,26 +313,24 @@ export class MutationQueue extends EventEmitter {
|
|
|
328
313
|
retryBackoff: this.config.retryBackoff,
|
|
329
314
|
},
|
|
330
315
|
commitLane: this.commitLane,
|
|
331
|
-
commitNotifications: this.commitNotifications,
|
|
332
316
|
commitMissingIds: this.commitMissingIds,
|
|
333
317
|
commitProcessing: this.commitProcessing,
|
|
334
318
|
setCommitProcessing: (value) => { this.commitProcessing = value; },
|
|
335
319
|
durableReplayBlock: this.durableReplayBlock,
|
|
336
320
|
sealDurableCommit: (input) => this.sealDurableCommit(input),
|
|
337
|
-
assertEnvelopeInsideReplayWindow: (envelope) => this.assertEnvelopeInsideReplayWindow(envelope),
|
|
321
|
+
assertEnvelopeInsideReplayWindow: (envelope) => { this.assertEnvelopeInsideReplayWindow(envelope); },
|
|
338
322
|
dispatchCommit: async (envelope) => this.parseMutationCommitResult(
|
|
339
323
|
await this.dispatchCommitBounded(envelope.operations, {
|
|
340
324
|
idempotencyKey: envelope.idempotencyKey,
|
|
341
325
|
...(envelope.commitOptions.reads ? { reads: envelope.commitOptions.reads } : {}),
|
|
342
|
-
...(envelope.commitOptions.track ? { track: envelope.commitOptions.track } : {}),
|
|
343
326
|
}),
|
|
344
327
|
),
|
|
345
328
|
persistDurableCommitAcceptance: (envelope, result) => this.persistDurableCommitAcceptance(envelope, result),
|
|
346
329
|
removeDurableCommit: (idempotencyKey) => this.removeDurableCommit(idempotencyKey),
|
|
347
330
|
queuedCommitEchoSyncId: (transaction) => this.queuedCommitEchoSyncId(transaction),
|
|
348
|
-
completeQueuedCommit: (transaction, syncId) => this.completeQueuedCommit(transaction, syncId),
|
|
349
|
-
scheduleReplicationLagTimeout: (transactionId, clientTxId, correlationId) => this.scheduleReplicationLagTimeout(transactionId, clientTxId, correlationId),
|
|
350
|
-
noteAck: (syncId) => this.noteAck(syncId),
|
|
331
|
+
completeQueuedCommit: (transaction, syncId) => { this.completeQueuedCommit(transaction, syncId); },
|
|
332
|
+
scheduleReplicationLagTimeout: (transactionId, clientTxId, correlationId) => { this.scheduleReplicationLagTimeout(transactionId, clientTxId, correlationId); },
|
|
333
|
+
noteAck: (syncId) => { this.noteAck(syncId); },
|
|
351
334
|
isDefinitiveRejection: (error) => this.isDefinitiveRejection(error),
|
|
352
335
|
isPermanentError: (error) => this.isPermanentError(error),
|
|
353
336
|
scheduleRetry: (delayMs) => {
|
|
@@ -357,14 +340,13 @@ export class MutationQueue extends EventEmitter {
|
|
|
357
340
|
void this.processCommitLane();
|
|
358
341
|
}, delayMs);
|
|
359
342
|
},
|
|
360
|
-
emitCommitLifecycle: (event, payload) => this.emitCommitLifecycle(event, payload),
|
|
343
|
+
emitCommitLifecycle: (event, payload) => { this.emitCommitLifecycle(event, payload); },
|
|
361
344
|
};
|
|
362
345
|
}
|
|
363
346
|
|
|
364
347
|
private get commitReceiptContext(): CommitReceiptContext {
|
|
365
348
|
return {
|
|
366
349
|
commitStore: this.commitStore,
|
|
367
|
-
commitNotifications: this.commitNotifications,
|
|
368
350
|
commitMissingIds: this.commitMissingIds,
|
|
369
351
|
replicationLagErrors: this.replicationLagErrors,
|
|
370
352
|
on: (event, listener) => this.on(event, listener),
|
|
@@ -374,15 +356,15 @@ export class MutationQueue extends EventEmitter {
|
|
|
374
356
|
|
|
375
357
|
private get commitApiContext(): CommitApiContext {
|
|
376
358
|
return {
|
|
377
|
-
assertDurableReplayOpen: () => this.assertDurableReplayOpen(),
|
|
359
|
+
assertDurableReplayOpen: () => { this.assertDurableReplayOpen(); },
|
|
378
360
|
commitStore: this.commitStore,
|
|
379
361
|
commitLane: this.commitLane,
|
|
380
362
|
replicationLagErrors: this.replicationLagErrors,
|
|
381
|
-
clearReplicationLagState: (transactionId) => this.clearReplicationLagState(transactionId),
|
|
363
|
+
clearReplicationLagState: (transactionId) => { this.clearReplicationLagState(transactionId); },
|
|
382
364
|
nextCommitSequence: () => this.nextCommitSequence(),
|
|
383
365
|
sealDurableCommit: (input) => this.sealDurableCommit(input),
|
|
384
366
|
processCommitLane: () => this.processCommitLane(),
|
|
385
|
-
emitCommitLifecycle: (event, payload) => this.emitCommitLifecycle(event, payload),
|
|
367
|
+
emitCommitLifecycle: (event, payload) => { this.emitCommitLifecycle(event, payload); },
|
|
386
368
|
};
|
|
387
369
|
}
|
|
388
370
|
|
|
@@ -390,7 +372,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
390
372
|
return {
|
|
391
373
|
enableOptimistic: this.config.enableOptimistic,
|
|
392
374
|
persistenceReady: !!this.persistence && !!this.commitOutboxScope,
|
|
393
|
-
assertDurableReplayOpen: () => this.assertDurableReplayOpen(),
|
|
375
|
+
assertDurableReplayOpen: () => { this.assertDurableReplayOpen(); },
|
|
394
376
|
generateId: () => this.generateId(),
|
|
395
377
|
normalizeModelKey,
|
|
396
378
|
computePriorityScore: (type, modelName) => this.computePriorityScore(type, modelName),
|
|
@@ -399,11 +381,11 @@ export class MutationQueue extends EventEmitter {
|
|
|
399
381
|
extractPreviousData: (model, input) => this.extractPreviousData(model, input),
|
|
400
382
|
mapChangesToInput: (modelName, changes) => this.mapChangesToInput(modelName, changes),
|
|
401
383
|
isReorderPayload: (input) => this.isReorderPayload(input),
|
|
402
|
-
attachConfirmation: (transaction) => this.attachConfirmation(transaction),
|
|
403
|
-
add: (transaction) => this.store.add(transaction),
|
|
404
|
-
applyOptimisticCreate: (model, transaction) => this.applyOptimisticCreate(model, transaction),
|
|
405
|
-
applyOptimisticUpdate: (model, transaction) => this.applyOptimisticUpdate(model, transaction),
|
|
406
|
-
applyOptimisticDelete: (model, transaction) => this.applyOptimisticDelete(model, transaction),
|
|
384
|
+
attachConfirmation: (transaction) => { this.attachConfirmation(transaction); },
|
|
385
|
+
add: (transaction) => { this.store.add(transaction); },
|
|
386
|
+
applyOptimisticCreate: (model, transaction) => { this.applyOptimisticCreate(model, transaction); },
|
|
387
|
+
applyOptimisticUpdate: (model, transaction) => { this.applyOptimisticUpdate(model, transaction); },
|
|
388
|
+
applyOptimisticDelete: (model, transaction) => { this.applyOptimisticDelete(model, transaction); },
|
|
407
389
|
takeUnsentCreateForModel: (modelName, modelId) => this.takeUnsentCreateForModel(modelName, modelId),
|
|
408
390
|
cancelUnsentCreateForDelete: (transaction) => this.cancelUnsentCreateForDelete(transaction),
|
|
409
391
|
completeLocalDelete: (model, context, writeOptions, sourceMutationIds) => this.completeLocalDelete(model, context, writeOptions, sourceMutationIds),
|
|
@@ -411,11 +393,11 @@ export class MutationQueue extends EventEmitter {
|
|
|
411
393
|
pendingMergeByModel: this.pendingMergeByModel,
|
|
412
394
|
inFlightByModel: this.inFlightByModel,
|
|
413
395
|
findCreateBarrierForDelete: (modelName, modelId) => this.findCreateBarrierForDelete(modelName, modelId),
|
|
414
|
-
deferDeleteUntilCreateSettles: (create, transaction) => this.deferDeleteUntilCreateSettles(create, transaction),
|
|
396
|
+
deferDeleteUntilCreateSettles: (create, transaction) => { this.deferDeleteUntilCreateSettles(create, transaction); },
|
|
415
397
|
logger: this.runtime.logger,
|
|
416
398
|
persistAndStage: (transaction, modelData) => this.persistAndStage(transaction, modelData),
|
|
417
399
|
persistQueuedTransaction: (transaction, modelData) => this.persistQueuedTransaction(transaction, modelData),
|
|
418
|
-
stageTransaction: (transaction) => this.stageTransaction(transaction),
|
|
400
|
+
stageTransaction: (transaction) => { this.stageTransaction(transaction); },
|
|
419
401
|
emit: (event, payload) => this.emit(event, payload),
|
|
420
402
|
};
|
|
421
403
|
}
|
|
@@ -425,9 +407,9 @@ export class MutationQueue extends EventEmitter {
|
|
|
425
407
|
executionQueue: this.executionQueue,
|
|
426
408
|
inFlightByModel: this.inFlightByModel,
|
|
427
409
|
pendingMergeByModel: this.pendingMergeByModel,
|
|
428
|
-
ensureDerivedFields: (transaction) => this.ensureDerivedFields(transaction),
|
|
429
|
-
scheduleProcessing: (immediate) => this.scheduleProcessing(immediate),
|
|
430
|
-
storeRemove: (transactionId) => this.store.remove(transactionId),
|
|
410
|
+
ensureDerivedFields: (transaction) => { this.ensureDerivedFields(transaction); },
|
|
411
|
+
scheduleProcessing: (immediate) => { this.scheduleProcessing(immediate); },
|
|
412
|
+
storeRemove: (transactionId) => { this.store.remove(transactionId); },
|
|
431
413
|
};
|
|
432
414
|
}
|
|
433
415
|
|
|
@@ -440,7 +422,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
440
422
|
isProcessing: this.isProcessing,
|
|
441
423
|
setIsProcessing: (value) => { this.isProcessing = value; },
|
|
442
424
|
takeNextExecutionBatch: () => this.takeNextExecutionBatch(),
|
|
443
|
-
ensureDerivedFields: (transaction) => this.ensureDerivedFields(transaction),
|
|
425
|
+
ensureDerivedFields: (transaction) => { this.ensureDerivedFields(transaction); },
|
|
444
426
|
ensureCommitEnvelope: (batch) => this.ensureCommitEnvelope([...batch]),
|
|
445
427
|
executingCount: this.executingCount,
|
|
446
428
|
setExecutingCount: (value) => { this.executingCount = value; },
|
|
@@ -449,31 +431,28 @@ export class MutationQueue extends EventEmitter {
|
|
|
449
431
|
generateId: () => this.generateId(),
|
|
450
432
|
computePriorityScore: (type, modelName) => this.computePriorityScore(type, modelName),
|
|
451
433
|
store: this.store,
|
|
452
|
-
enqueue: (transaction) => this.enqueue(transaction),
|
|
434
|
+
enqueue: (transaction) => { this.enqueue(transaction); },
|
|
453
435
|
optimisticUpdates: this.localMutationPort.updates,
|
|
454
|
-
commitNotifications: this.commitNotifications,
|
|
455
436
|
commitMissingIds: this.commitMissingIds,
|
|
456
437
|
sourceMutationIdsFor: (batch) => this.sourceMutationIdsFor(batch),
|
|
457
438
|
dispatchCommitBounded: (...args) => this.dispatchCommitBounded(...args),
|
|
458
439
|
parseMutationCommitResult: (value) => this.parseMutationCommitResult(value),
|
|
459
440
|
persistDurableCommitAcceptance: (envelope, result) => this.persistDurableCommitAcceptance(envelope, result),
|
|
460
441
|
removeDurableCommit: (idempotencyKey) => this.removeDurableCommit(idempotencyKey),
|
|
461
|
-
assertEnvelopeInsideReplayWindow: (envelope) => this.assertEnvelopeInsideReplayWindow(envelope),
|
|
442
|
+
assertEnvelopeInsideReplayWindow: (envelope) => { this.assertEnvelopeInsideReplayWindow(envelope); },
|
|
462
443
|
sealDurableCommit: (input) => this.sealDurableCommit(input),
|
|
463
|
-
noteAck: (syncId) => this.noteAck(syncId),
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
clearReplicationLagState: (transactionId) => this.clearReplicationLagState(transactionId),
|
|
469
|
-
completeQueuedCommit: (transaction, syncId) => this.completeQueuedCommit(transaction, syncId),
|
|
444
|
+
noteAck: (syncId) => { this.noteAck(syncId); },
|
|
445
|
+
scheduleReplicationLagTimeout: (transactionId, clientTxId, correlationId) => { this.scheduleReplicationLagTimeout(transactionId, clientTxId, correlationId); },
|
|
446
|
+
scheduleDeltaConfirmationTimeout: (transaction, timeoutMs) => { this.scheduleDeltaConfirmationTimeout(transaction, timeoutMs); },
|
|
447
|
+
clearReplicationLagState: (transactionId) => { this.clearReplicationLagState(transactionId); },
|
|
448
|
+
completeQueuedCommit: (transaction, syncId) => { this.completeQueuedCommit(transaction, syncId); },
|
|
470
449
|
queuedCommitMatchesCorrelation: (transaction, correlationId) => this.queuedCommitMatchesCorrelation(transaction, correlationId),
|
|
471
450
|
recentDeltaCorrelations: this.recentDeltaCorrelations,
|
|
472
451
|
lastSeenSyncId: this.lastSeenSyncId,
|
|
473
|
-
scheduleProcessing: (immediate) => this.scheduleProcessing(immediate),
|
|
452
|
+
scheduleProcessing: (immediate) => { this.scheduleProcessing(immediate); },
|
|
474
453
|
handleFailure: (transaction, error) => this.handleFailure(transaction, error),
|
|
475
454
|
isDefinitiveRejection: (error) => this.isDefinitiveRejection(error),
|
|
476
|
-
emitCommitLifecycle: (event, payload) => this.emitCommitLifecycle(event, payload),
|
|
455
|
+
emitCommitLifecycle: (event, payload) => { this.emitCommitLifecycle(event, payload); },
|
|
477
456
|
emit: (event, payload) => this.emit(event, payload),
|
|
478
457
|
rollbackOptimistic: (transaction, reason, error) => this.rollbackOptimistic(transaction, reason, error),
|
|
479
458
|
};
|
|
@@ -486,20 +465,20 @@ export class MutationQueue extends EventEmitter {
|
|
|
486
465
|
store: this.store,
|
|
487
466
|
isPermanentError: (error) => this.isPermanentError(error),
|
|
488
467
|
rollbackOptimistic: (transaction, reason, error) => this.rollbackOptimistic(transaction, reason, error),
|
|
489
|
-
enqueue: (transaction) => this.enqueue(transaction),
|
|
468
|
+
enqueue: (transaction) => { this.enqueue(transaction); },
|
|
490
469
|
getLastPermanentErrorSignature: () => this.lastPermanentErrorSig,
|
|
491
470
|
setLastPermanentErrorSignature: (signature) => { this.lastPermanentErrorSig = signature; },
|
|
492
471
|
emit: (event, payload) => this.emit(event, payload),
|
|
493
472
|
};
|
|
494
473
|
}
|
|
495
474
|
|
|
496
|
-
private get
|
|
475
|
+
private get conflictResolutionContext(): ConflictResolutionContext {
|
|
497
476
|
return {
|
|
498
477
|
config: this.config,
|
|
499
478
|
store: this.store,
|
|
500
479
|
rollbackOptimistic: (transaction, reason) => this.rollbackOptimistic(transaction, reason),
|
|
501
480
|
mergeData: (local, remote) => this.mergeData(local, remote),
|
|
502
|
-
enqueue: (transaction) => this.enqueue(transaction),
|
|
481
|
+
enqueue: (transaction) => { this.enqueue(transaction); },
|
|
503
482
|
};
|
|
504
483
|
}
|
|
505
484
|
|
|
@@ -524,21 +503,21 @@ export class MutationQueue extends EventEmitter {
|
|
|
524
503
|
store: this.store,
|
|
525
504
|
executionQueue: this.executionQueue,
|
|
526
505
|
optimisticUpdates: this.localMutationPort.updates,
|
|
527
|
-
assertDurableReplayOpen: () => this.assertDurableReplayOpen(),
|
|
506
|
+
assertDurableReplayOpen: () => { this.assertDurableReplayOpen(); },
|
|
528
507
|
processCommitLane: () => this.processCommitLane(),
|
|
529
508
|
takePendingDrainBatch: (pending) => this.takePendingDrainBatch(pending),
|
|
530
509
|
ensureCommitEnvelope: (batch) => this.ensureCommitEnvelope(batch),
|
|
531
|
-
ensureDerivedFields: (transaction) => this.ensureDerivedFields(transaction),
|
|
510
|
+
ensureDerivedFields: (transaction) => { this.ensureDerivedFields(transaction); },
|
|
532
511
|
sourceMutationIdsFor: (batch) => this.sourceMutationIdsFor(batch),
|
|
533
512
|
sealDurableCommit: (input) => this.sealDurableCommit(input),
|
|
534
|
-
assertEnvelopeInsideReplayWindow: (envelope) => this.assertEnvelopeInsideReplayWindow(envelope),
|
|
513
|
+
assertEnvelopeInsideReplayWindow: (envelope) => { this.assertEnvelopeInsideReplayWindow(envelope); },
|
|
535
514
|
parseMutationCommitResult: (value) => this.parseMutationCommitResult(value),
|
|
536
515
|
dispatchCommitBounded: (...args) => this.dispatchCommitBounded(...args),
|
|
537
516
|
persistDurableCommitAcceptance: (envelope, result) => this.persistDurableCommitAcceptance(envelope, result),
|
|
538
517
|
removeDurableCommit: (idempotencyKey) => this.removeDurableCommit(idempotencyKey),
|
|
539
|
-
scheduleReplicationLagTimeout: (transactionId, clientTxId, correlationId) => this.scheduleReplicationLagTimeout(transactionId, clientTxId, correlationId),
|
|
540
|
-
scheduleDeltaConfirmationTimeout: (transaction, timeoutMs) => this.scheduleDeltaConfirmationTimeout(transaction, timeoutMs),
|
|
541
|
-
enqueue: (transaction) => this.enqueue(transaction),
|
|
518
|
+
scheduleReplicationLagTimeout: (transactionId, clientTxId, correlationId) => { this.scheduleReplicationLagTimeout(transactionId, clientTxId, correlationId); },
|
|
519
|
+
scheduleDeltaConfirmationTimeout: (transaction, timeoutMs) => { this.scheduleDeltaConfirmationTimeout(transaction, timeoutMs); },
|
|
520
|
+
enqueue: (transaction) => { this.enqueue(transaction); },
|
|
542
521
|
recentDeltaCorrelations: this.recentDeltaCorrelations,
|
|
543
522
|
emit: (event, payload) => this.emit(event, payload),
|
|
544
523
|
};
|
|
@@ -564,7 +543,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
564
543
|
commitOutboxScope: this.commitOutboxScope,
|
|
565
544
|
config: this.config,
|
|
566
545
|
store: this.store,
|
|
567
|
-
enqueue: (transaction) => this.enqueue(transaction),
|
|
546
|
+
enqueue: (transaction) => { this.enqueue(transaction); },
|
|
568
547
|
computePriorityScore: (type, modelName) => this.computePriorityScore(type, modelName),
|
|
569
548
|
deserializeTransaction: (data) => this.deserializeTransaction(data),
|
|
570
549
|
};
|
|
@@ -637,80 +616,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
637
616
|
return entityKey(modelName, modelId);
|
|
638
617
|
}
|
|
639
618
|
|
|
640
|
-
|
|
641
|
-
private receiptTargetKey(modelName: string, modelId: string): string {
|
|
642
|
-
return stableStringify([normalizeModelKey(modelName), modelId]);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
/**
|
|
646
|
-
* Relates stale notifications back to write targets without assuming the
|
|
647
|
-
* server's canonical model name uses the same spelling as the public schema
|
|
648
|
-
* key (`Item` versus `items`). Exact `(model,id)` wins; a globally unique id
|
|
649
|
-
* is the compatibility fallback. An ambiguous same-id cross-model mismatch
|
|
650
|
-
* is deliberately left unclassified, so it cannot falsely settle a queued
|
|
651
|
-
* write. A notification with no write-target id (or an explicit group) is a
|
|
652
|
-
* declared-read conflict and holds the whole batch.
|
|
653
|
-
*/
|
|
654
|
-
private classifyReceiptNotifications(
|
|
655
|
-
operations: readonly { model: string; id: string }[],
|
|
656
|
-
notifications: readonly StaleNotification[],
|
|
657
|
-
): {
|
|
658
|
-
holdsEntireBatch: boolean;
|
|
659
|
-
heldTargets: Set<string>;
|
|
660
|
-
notificationsByTarget: Map<string, StaleNotification[]>;
|
|
661
|
-
} {
|
|
662
|
-
const targets = operations.map((operation) => ({
|
|
663
|
-
id: operation.id,
|
|
664
|
-
key: this.receiptTargetKey(operation.model, operation.id),
|
|
665
|
-
}));
|
|
666
|
-
const heldTargets = new Set<string>();
|
|
667
|
-
const notificationsByTarget = new Map<string, StaleNotification[]>();
|
|
668
|
-
let holdsEntireBatch = false;
|
|
669
|
-
|
|
670
|
-
for (const notification of notifications) {
|
|
671
|
-
// Scope is decided before any target matching. A group premise fires over
|
|
672
|
-
// the WHOLE batch by convention, and its `target` now names the row that
|
|
673
|
-
// actually moved — which may well be a row this batch is writing, so
|
|
674
|
-
// matching first would misread a batch-wide hold as a per-row one.
|
|
675
|
-
if (notification.scope === 'group') {
|
|
676
|
-
holdsEntireBatch = true;
|
|
677
|
-
continue;
|
|
678
|
-
}
|
|
679
|
-
const candidates = targets.filter(
|
|
680
|
-
(target) => target.id === notification.target.id,
|
|
681
|
-
);
|
|
682
|
-
const notificationKey = this.receiptTargetKey(
|
|
683
|
-
notification.target.model,
|
|
684
|
-
notification.target.id,
|
|
685
|
-
);
|
|
686
|
-
const exactTargets = candidates.filter(
|
|
687
|
-
(target) => target.key === notificationKey,
|
|
688
|
-
);
|
|
689
|
-
const candidateKeys = new Set(
|
|
690
|
-
(exactTargets.length > 0 ? exactTargets : candidates).map(
|
|
691
|
-
(target) => target.key,
|
|
692
|
-
),
|
|
693
|
-
);
|
|
694
|
-
|
|
695
|
-
if (candidates.length === 0) {
|
|
696
|
-
holdsEntireBatch = true;
|
|
697
|
-
continue;
|
|
698
|
-
}
|
|
699
|
-
if (candidateKeys.size !== 1) {
|
|
700
|
-
// Same id across multiple differently named models with no exact match:
|
|
701
|
-
// the id-only compatibility fallback is ambiguous. Await the echo.
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
const [targetKey] = candidateKeys;
|
|
705
|
-
if (!targetKey) continue;
|
|
706
|
-
heldTargets.add(targetKey);
|
|
707
|
-
const targetNotifications = notificationsByTarget.get(targetKey) ?? [];
|
|
708
|
-
targetNotifications.push(notification);
|
|
709
|
-
notificationsByTarget.set(targetKey, targetNotifications);
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return { holdsEntireBatch, heldTargets, notificationsByTarget };
|
|
713
|
-
}
|
|
619
|
+
/* Stale receipt classification was removed; stale premises now reject. */
|
|
714
620
|
|
|
715
621
|
private resolveConfirmation(transaction: QueuedMutation): void {
|
|
716
622
|
const resolver = this.confirmationResolvers.get(transaction.id);
|
|
@@ -821,11 +727,6 @@ export class MutationQueue extends EventEmitter {
|
|
|
821
727
|
|
|
822
728
|
private readonly localMutationPort: LocalMutationPort;
|
|
823
729
|
|
|
824
|
-
// Stale-context notifications, keyed by transaction id. When the server
|
|
825
|
-
// accepts a commit but reports that an operation's premise had moved,
|
|
826
|
-
// the notification lands here from the commit acknowledgement and is drained
|
|
827
|
-
// by `waitForCommitReceipt`, so the receipt can carry it back to the caller.
|
|
828
|
-
private commitNotifications = new Map<string, StaleNotification[]>();
|
|
829
730
|
/** Zero-row targets returned on a successful atomic commit receipt. */
|
|
830
731
|
private commitMissingIds = new Map<string, string[]>();
|
|
831
732
|
|
|
@@ -1688,7 +1589,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
1688
1589
|
async enqueueCommit(
|
|
1689
1590
|
clientTxId: string,
|
|
1690
1591
|
operations: CommitTransaction['operations'],
|
|
1691
|
-
options: { reads?: ReadDependency[] | null
|
|
1592
|
+
options: { reads?: ReadDependency[] | null } = {},
|
|
1692
1593
|
): Promise<void> {
|
|
1693
1594
|
return enqueueCommit(this.commitApiContext, clientTxId, operations, options);
|
|
1694
1595
|
}
|
|
@@ -1701,8 +1602,8 @@ export class MutationQueue extends EventEmitter {
|
|
|
1701
1602
|
clientTxId: string,
|
|
1702
1603
|
): Promise<{
|
|
1703
1604
|
lastSyncId: number;
|
|
1704
|
-
notifications?: StaleNotification[];
|
|
1705
1605
|
missingIds?: string[];
|
|
1606
|
+
operationResults?: CommitOperationResult[];
|
|
1706
1607
|
}> {
|
|
1707
1608
|
return waitForCommitReceipt(this.commitReceiptContext, clientTxId);
|
|
1708
1609
|
}
|
|
@@ -1725,7 +1626,7 @@ export class MutationQueue extends EventEmitter {
|
|
|
1725
1626
|
}
|
|
1726
1627
|
|
|
1727
1628
|
async handleConflict(transaction: QueuedMutation, serverData: MutationInput): Promise<void> {
|
|
1728
|
-
await resolveConflict(this.
|
|
1629
|
+
await resolveConflict(this.conflictResolutionContext, transaction, serverData);
|
|
1729
1630
|
}
|
|
1730
1631
|
|
|
1731
1632
|
private applyOptimisticCreate(model: LocalModel, transaction: QueuedMutation): void {
|
|
@@ -1879,47 +1780,33 @@ export class MutationQueue extends EventEmitter {
|
|
|
1879
1780
|
}
|
|
1880
1781
|
|
|
1881
1782
|
/** Generates a unique local transaction id. */
|
|
1783
|
+
// The payload rules live in `mutationInput`; these keep the call sites here
|
|
1784
|
+
// reading as the queue's own vocabulary.
|
|
1882
1785
|
private generateId(): string {
|
|
1883
|
-
return
|
|
1786
|
+
return generateTransactionId();
|
|
1884
1787
|
}
|
|
1885
1788
|
|
|
1886
1789
|
private mergeData(
|
|
1887
1790
|
local: MutationInput | undefined,
|
|
1888
1791
|
remote: MutationInput | undefined
|
|
1889
1792
|
): MutationInput {
|
|
1890
|
-
return
|
|
1793
|
+
return mergeMutationData(local, remote);
|
|
1891
1794
|
}
|
|
1892
1795
|
|
|
1893
1796
|
private extractCreateData(model: LocalModel): MutationInput {
|
|
1894
|
-
return
|
|
1797
|
+
return createDataFor(model, this.runtime);
|
|
1895
1798
|
}
|
|
1896
1799
|
|
|
1897
1800
|
private mapChangesToInput(modelName: string, changes: Record<string, unknown>): MutationInput {
|
|
1898
|
-
return
|
|
1801
|
+
return changesToInput(modelName, changes, this.runtime);
|
|
1899
1802
|
}
|
|
1900
1803
|
|
|
1901
1804
|
private extractUpdateData(model: LocalModel): MutationInput {
|
|
1902
|
-
return
|
|
1805
|
+
return updateDataFor(model, this.runtime);
|
|
1903
1806
|
}
|
|
1904
1807
|
|
|
1905
|
-
// Derive previous values for changed fields to support accurate rollback.
|
|
1906
|
-
// Model-specific special cases do not belong here; a model that needs to
|
|
1907
|
-
// surface previous state beyond `modifiedProperties` should expose a typed
|
|
1908
|
-
// `getPreviousData()` accessor for this method to call.
|
|
1909
1808
|
private extractPreviousData(model: LocalModel, updateInput?: MutationInput): MutationInput {
|
|
1910
|
-
|
|
1911
|
-
// exactly those keys, so the recorded undo inverse reverts them and nothing
|
|
1912
|
-
// else — a full-row inverse would clobber concurrent edits to unrelated
|
|
1913
|
-
// fields. `fallbackToLive: false` makes `Model.capturePreviousValues` omit
|
|
1914
|
-
// any key it cannot resolve, and `buildUndoOps` then drops an un-revertible
|
|
1915
|
-
// inverse rather than inventing one. With no `updateInput` (a full extract)
|
|
1916
|
-
// it falls back to every tracked field. `Model.capturePreviousValues` is the
|
|
1917
|
-
// single before-image source, shared with
|
|
1918
|
-
// `RecordingMutation.snapshotFields`.
|
|
1919
|
-
const keys = updateInput
|
|
1920
|
-
? Object.keys(updateInput)
|
|
1921
|
-
: [...(model.modifiedProperties instanceof Map ? model.modifiedProperties.keys() : [])];
|
|
1922
|
-
return { id: model.id, ...model.capturePreviousValues(keys, { fallbackToLive: false }) };
|
|
1809
|
+
return previousDataFor(model, updateInput);
|
|
1923
1810
|
}
|
|
1924
1811
|
|
|
1925
1812
|
/** Returns a snapshot of queue counts and the current configuration. */
|
|
@@ -2040,7 +1927,6 @@ export class MutationQueue extends EventEmitter {
|
|
|
2040
1927
|
this.recentDeltaCorrelations.clear();
|
|
2041
1928
|
this.commitLane = [];
|
|
2042
1929
|
this.commitStore.clear();
|
|
2043
|
-
this.commitNotifications.clear();
|
|
2044
1930
|
this.commitMissingIds.clear();
|
|
2045
1931
|
|
|
2046
1932
|
// Clear event listeners
|
|
@@ -3,10 +3,9 @@ import type { MutationQueueConfig } from './MutationQueue.js';
|
|
|
3
3
|
import type { MutationInput, QueuedMutation } from './commitPayload.js';
|
|
4
4
|
import type { MutationStore } from './MutationStore.js';
|
|
5
5
|
import type { OptimisticUpdateEntry } from './localMutation.js';
|
|
6
|
-
import type { StaleNotification } from '@abloatai/transaction/coordination/schema';
|
|
7
6
|
import type { DeltaConfirmationTracker } from './deltaConfirmation.js';
|
|
8
|
-
import type { MutationCommitResult } from '@abloatai/transaction/
|
|
9
|
-
import type { DurableCommitEnvelope } from '@abloatai/transaction/
|
|
7
|
+
import type { MutationCommitResult } from '@abloatai/transaction/commit';
|
|
8
|
+
import type { DurableCommitEnvelope } from '@abloatai/transaction/commit';
|
|
10
9
|
import { AbloError, AbloNotFoundError } from '@abloatai/transaction/errors';
|
|
11
10
|
import { applyWriteOptions, normalizeModelKey, TX_TYPE_TO_MUTATION_OP, type WriteOperationFields } from './commitPayload.js';
|
|
12
11
|
import type { MutationOperationType } from '@abloatai/transaction/types';
|
|
@@ -30,7 +29,6 @@ export interface BatchProcessingContext {
|
|
|
30
29
|
readonly store: MutationStore;
|
|
31
30
|
readonly enqueue: (transaction: QueuedMutation) => void;
|
|
32
31
|
readonly optimisticUpdates: Map<string, OptimisticUpdateEntry>;
|
|
33
|
-
readonly commitNotifications: Map<string, StaleNotification[]>;
|
|
34
32
|
readonly commitMissingIds: Map<string, string[]>;
|
|
35
33
|
readonly sourceMutationIdsFor: (batch: readonly QueuedMutation[]) => string[];
|
|
36
34
|
readonly dispatchCommitBounded: (...args: Parameters<import('../../interfaces/index.js').MutationExecutor['commit']>) => ReturnType<import('../../interfaces/index.js').MutationExecutor['commit']>;
|
|
@@ -40,11 +38,6 @@ export interface BatchProcessingContext {
|
|
|
40
38
|
readonly assertEnvelopeInsideReplayWindow: (envelope: DurableCommitEnvelope) => void;
|
|
41
39
|
readonly sealDurableCommit: (input: Parameters<typeof import('./commitTransport.js').sealDurableCommit>[1]) => Promise<DurableCommitEnvelope>;
|
|
42
40
|
readonly noteAck: (syncId: number | undefined) => void;
|
|
43
|
-
readonly classifyReceiptNotifications: (
|
|
44
|
-
operations: readonly { model: string; id: string }[],
|
|
45
|
-
notifications: readonly StaleNotification[],
|
|
46
|
-
) => { holdsEntireBatch: boolean; heldTargets: Set<string>; notificationsByTarget: Map<string, StaleNotification[]> };
|
|
47
|
-
readonly receiptTargetKey: (modelName: string, modelId: string) => string;
|
|
48
41
|
readonly scheduleReplicationLagTimeout: (transactionId: string, clientTxId?: string, correlationId?: string) => void;
|
|
49
42
|
readonly scheduleDeltaConfirmationTimeout: (transaction: QueuedMutation, timeoutMs: number) => void;
|
|
50
43
|
readonly clearReplicationLagState: (transactionId: string) => void;
|
|
@@ -170,24 +163,6 @@ export async function processBatch(ctx: BatchProcessingContext): Promise<void> {
|
|
|
170
163
|
);
|
|
171
164
|
const lastSyncId = result.lastSyncId;
|
|
172
165
|
|
|
173
|
-
const notifications = result.notifications;
|
|
174
|
-
const {
|
|
175
|
-
holdsEntireBatch,
|
|
176
|
-
heldTargets,
|
|
177
|
-
notificationsByTarget,
|
|
178
|
-
} = ctx.classifyReceiptNotifications(
|
|
179
|
-
batchOps.map(({ op }) => ({ model: op.model, id: op.id })),
|
|
180
|
-
notifications ?? [],
|
|
181
|
-
);
|
|
182
|
-
for (const { tx } of batchOps) {
|
|
183
|
-
const txTarget = ctx.receiptTargetKey(tx.modelKey, tx.modelId);
|
|
184
|
-
const txNotifs = holdsEntireBatch
|
|
185
|
-
? notifications
|
|
186
|
-
: notificationsByTarget.get(txTarget);
|
|
187
|
-
if (txNotifs && txNotifs.length > 0) {
|
|
188
|
-
ctx.commitNotifications.set(tx.id, txNotifs);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
166
|
const missingIds = new Set(result.missingIds ?? []);
|
|
192
167
|
const settlingBatchOps: typeof batchOps = [];
|
|
193
168
|
for (const entry of batchOps) {
|
|
@@ -202,17 +177,6 @@ export async function processBatch(ctx: BatchProcessingContext): Promise<void> {
|
|
|
202
177
|
);
|
|
203
178
|
continue;
|
|
204
179
|
}
|
|
205
|
-
if (
|
|
206
|
-
holdsEntireBatch ||
|
|
207
|
-
heldTargets.has(ctx.receiptTargetKey(tx.modelKey, tx.modelId))
|
|
208
|
-
) {
|
|
209
|
-
await ctx.rollbackOptimistic(tx, 'conflict_server_wins');
|
|
210
|
-
ctx.store.updateStatus(tx.id, 'completed');
|
|
211
|
-
ctx.emit('transaction:completed', tx);
|
|
212
|
-
ctx.emit(`transaction:completed:${tx.id}`, tx);
|
|
213
|
-
ctx.optimisticUpdates.delete(tx.id);
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
180
|
settlingBatchOps.push(entry);
|
|
217
181
|
}
|
|
218
182
|
|