@abloatai/humans 0.37.0 → 0.38.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 (35) hide show
  1. package/dist/core.d.ts +1 -0
  2. package/dist/core.js +4 -0
  3. package/dist/local/BaseSyncedStore.d.ts +4 -2
  4. package/dist/local/client/createModelProxy.js +14 -12
  5. package/dist/local/client/options.d.ts +7 -0
  6. package/dist/local/client/reactiveEngine.js +5 -3
  7. package/dist/local/client/storeLifecycle.js +6 -3
  8. package/dist/local/stores/DatabaseManager.d.ts +2 -2
  9. package/dist/local/stores/DatabaseManager.js +2 -2
  10. package/dist/local/stores/persistenceIdentity.d.ts +7 -8
  11. package/dist/local/stores/persistenceIdentity.js +4 -5
  12. package/dist/local/sync/SyncWebSocket.d.ts +7 -0
  13. package/dist/local/sync/SyncWebSocket.js +18 -0
  14. package/dist/local/sync/deltaPipeline.js +38 -13
  15. package/dist/local/sync/drainProfile.d.ts +59 -0
  16. package/dist/local/sync/drainProfile.js +127 -0
  17. package/dist/local/sync/initialize.js +2 -2
  18. package/dist/local/transactions/mutations/MutationQueue.js +32 -12
  19. package/dist/local/transactions/mutations/pendingDrain.d.ts +1 -1
  20. package/dist/local/transactions/mutations/pendingDrain.js +2 -1
  21. package/package.json +2 -2
  22. package/src/core.ts +12 -0
  23. package/src/local/BaseSyncedStore.ts +4 -2
  24. package/src/local/client/createModelProxy.ts +14 -12
  25. package/src/local/client/options.ts +9 -0
  26. package/src/local/client/reactiveEngine.ts +5 -2
  27. package/src/local/client/storeLifecycle.ts +10 -4
  28. package/src/local/stores/DatabaseManager.ts +4 -4
  29. package/src/local/stores/persistenceIdentity.ts +10 -12
  30. package/src/local/sync/SyncWebSocket.ts +17 -0
  31. package/src/local/sync/deltaPipeline.ts +48 -21
  32. package/src/local/sync/drainProfile.ts +164 -0
  33. package/src/local/sync/initialize.ts +2 -2
  34. package/src/local/transactions/mutations/MutationQueue.ts +31 -12
  35. package/src/local/transactions/mutations/pendingDrain.ts +7 -2
@@ -1222,17 +1222,20 @@ export class MutationQueue extends EventEmitter {
1222
1222
  * transaction.
1223
1223
  */
1224
1224
  confirmationFor(modelName: string, modelId: string): Promise<void> {
1225
- const candidates = [
1226
- ...this.store.getByStatus('pending'),
1227
- ...this.store.getByStatus('executing'),
1228
- ...this.store.getByStatus('awaiting_delta'),
1229
- ].filter(
1230
- (tx) => tx.modelName === modelName && tx.modelId === modelId,
1231
- );
1232
- if (candidates.length === 0) return Promise.resolve();
1233
- const latest = candidates.sort((a, b) => b.createdAt - a.createdAt)[0];
1234
- if (!latest) return Promise.resolve();
1235
- return latest.confirmation ?? Promise.resolve();
1225
+ const transactions = this.store.getAll();
1226
+ for (let index = transactions.length - 1; index >= 0; index--) {
1227
+ const transaction = transactions[index];
1228
+ if (
1229
+ transaction?.modelName === modelName &&
1230
+ transaction.modelId === modelId &&
1231
+ (transaction.status === 'pending' ||
1232
+ transaction.status === 'executing' ||
1233
+ transaction.status === 'awaiting_delta')
1234
+ ) {
1235
+ return transaction.confirmation ?? Promise.resolve();
1236
+ }
1237
+ }
1238
+ return Promise.resolve();
1236
1239
  }
1237
1240
 
1238
1241
  /**
@@ -1425,7 +1428,23 @@ export class MutationQueue extends EventEmitter {
1425
1428
  }
1426
1429
 
1427
1430
  private async drainPendingInternal(): Promise<void> {
1428
- await drainPendingSettlements(this.pendingDrainContext);
1431
+ // The normal batch scheduler and the explicit/reconnect drain are two
1432
+ // ways to drive the same durable queue. They must never seal the same
1433
+ // staged source records concurrently: the first seal consumes those
1434
+ // records, so the second would correctly reject them as already claimed.
1435
+ //
1436
+ // `isProcessing` is acquired synchronously before either path awaits,
1437
+ // making it the queue-wide execution lock. If the normal lane already
1438
+ // owns it, that lane will finish the pending work; callers waiting on a
1439
+ // specific confirmation remain attached to the exact transaction.
1440
+ if (this.isProcessing) return;
1441
+ this.isProcessing = true;
1442
+ try {
1443
+ await drainPendingSettlements(this.pendingDrainContext);
1444
+ } finally {
1445
+ this.isProcessing = false;
1446
+ if (this.executionQueue.length > 0) this.scheduleProcessing(true);
1447
+ }
1429
1448
  }
1430
1449
  async create(
1431
1450
  model: LocalModel,
@@ -10,7 +10,7 @@ export interface PendingDrainContext {
10
10
  readonly runtime: RuntimeContext;
11
11
  readonly config: { deltaConfirmationTimeout: number };
12
12
  readonly store: MutationStore;
13
- executionQueue: QueuedMutation[];
13
+ readonly executionQueue: QueuedMutation[];
14
14
  readonly optimisticUpdates: Map<string, OptimisticUpdateEntry>;
15
15
  readonly assertDurableReplayOpen: () => void;
16
16
  readonly processCommitLane: () => Promise<void>;
@@ -45,9 +45,14 @@ export async function drainPendingSettlements(ctx: PendingDrainContext): Promise
45
45
  // These rows may already be waiting behind the normal batch timer. The
46
46
  // reconnect fast path takes ownership of them for this attempt so the same
47
47
  // transaction cannot dispatch concurrently through both paths.
48
- ctx.executionQueue = ctx.executionQueue.filter(
48
+ const retainedQueue = ctx.executionQueue.filter(
49
49
  (tx) => !pendingIds.has(tx.id),
50
50
  );
51
+ ctx.executionQueue.splice(
52
+ 0,
53
+ ctx.executionQueue.length,
54
+ ...retainedQueue,
55
+ );
51
56
 
52
57
  const remaining = [...pending];
53
58
  while (remaining.length > 0) {