@abloatai/humans 0.59.1 → 0.59.2

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 (31) hide show
  1. package/dist/humans.d.ts +1 -1
  2. package/dist/local/BaseSyncedStore.d.ts +1 -3
  3. package/dist/local/BaseSyncedStore.js +2 -7
  4. package/dist/local/Database.d.ts +2 -2
  5. package/dist/local/LazyReferenceCollection.d.ts +1 -1
  6. package/dist/local/Model.js +2 -2
  7. package/dist/local/SyncClient.d.ts +4 -4
  8. package/dist/local/SyncClient.js +20 -1
  9. package/dist/local/stores/syncAction.d.ts +4 -4
  10. package/dist/local/sync/deltaPipeline.d.ts +11 -3
  11. package/dist/local/sync/deltaPipeline.js +27 -80
  12. package/dist/local/sync/schemas.d.ts +10 -10
  13. package/dist/local/transactions/mutations/MutationQueue.d.ts +3 -3
  14. package/dist/local/transactions/mutations/commitPayload.d.ts +1 -1
  15. package/dist/local/transactions/mutations/failureHandling.js +9 -81
  16. package/dist/local/transactions/mutations/failureReporting.d.ts +10 -0
  17. package/dist/local/transactions/mutations/failureReporting.js +67 -0
  18. package/dist/local/transactions/mutations/replayValidation.d.ts +15 -15
  19. package/dist/react/AbloProvider.js +1 -1
  20. package/dist/react/ClientSideSuspense.d.ts +1 -1
  21. package/dist/react/DefaultFallback.d.ts +1 -1
  22. package/dist/react/createAbloReact.js +1 -1
  23. package/dist/surface.d.ts +3 -3
  24. package/package.json +2 -2
  25. package/src/local/BaseSyncedStore.ts +2 -8
  26. package/src/local/Model.ts +2 -2
  27. package/src/local/SyncClient.ts +22 -1
  28. package/src/local/sync/SyncWebSocket.ts +1 -1
  29. package/src/local/sync/deltaPipeline.ts +26 -82
  30. package/src/local/transactions/mutations/failureHandling.ts +73 -132
  31. package/src/local/transactions/mutations/failureReporting.ts +93 -0
@@ -0,0 +1,93 @@
1
+ import { AbloError } from '@abloatai/transaction/errors';
2
+ import type { RuntimeContext } from '../../RuntimeContext.js';
3
+ import type { QueuedMutation } from './commitPayload.js';
4
+
5
+ const EXPECTED_COORDINATION_CODES = new Set([
6
+ 'stale_context',
7
+ 'claim_conflict',
8
+ 'claim_queued',
9
+ 'claim_lost',
10
+ 'entity_claimed',
11
+ 'model_claimed',
12
+ ]);
13
+
14
+ export interface PermanentFailureReportingContext {
15
+ readonly runtime: RuntimeContext;
16
+ readonly enableOptimistic: boolean;
17
+ readonly getLastPermanentErrorSignature: () => string | undefined;
18
+ readonly setLastPermanentErrorSignature: (signature: string) => void;
19
+ }
20
+
21
+ /** Report a terminal rejection at a severity that matches its meaning. */
22
+ export function reportPermanentMutationFailure(
23
+ ctx: PermanentFailureReportingContext,
24
+ transaction: QueuedMutation,
25
+ error: Error,
26
+ ): void {
27
+ try {
28
+ const abloError = error instanceof AbloError ? error : undefined;
29
+ const details = {
30
+ txId: transaction.id.slice(0, 8),
31
+ type: transaction.type,
32
+ model: transaction.modelName,
33
+ modelId: transaction.modelId.slice(0, 12),
34
+ errorType: abloError?.type ?? error.name,
35
+ errorCode: abloError?.code,
36
+ httpStatus: abloError?.httpStatus,
37
+ requestId: abloError?.requestId,
38
+ message: error.message,
39
+ inputKeys: transaction.data ? Object.keys(transaction.data) : undefined,
40
+ };
41
+ const signature = `${details.type}:${details.model}:${details.modelId}:${details.errorCode ?? details.errorType}`;
42
+ const isRepeat = signature === ctx.getLastPermanentErrorSignature();
43
+ ctx.setLastPermanentErrorSignature(signature);
44
+
45
+ const logger = ctx.runtime.logger;
46
+ if (isRepeat) {
47
+ logger.debug('write rejected again (same reason)', details);
48
+ return;
49
+ }
50
+
51
+ const isBenignIdempotent =
52
+ transaction.type === 'create' &&
53
+ (abloError?.code === 'unique_violation' ||
54
+ abloError?.type === 'AbloIdempotencyError');
55
+ if (isBenignIdempotent) {
56
+ logger.info(
57
+ `Your ${transaction.type} to "${transaction.modelName}" was skipped — this row already exists.`,
58
+ );
59
+ logger.debug('idempotent skip — details', details);
60
+ return;
61
+ }
62
+
63
+ if (abloError?.code && EXPECTED_COORDINATION_CODES.has(abloError.code)) {
64
+ const reverted = ctx.enableOptimistic
65
+ ? ' The local edit was reverted.'
66
+ : '';
67
+ const explanation =
68
+ abloError.code === 'stale_context'
69
+ ? 'it changed elsewhere before this save completed'
70
+ : 'another participant currently owns the conflicting work';
71
+ logger.info(
72
+ `Your ${transaction.type} to "${transaction.modelName}" was not saved because ${explanation}.${reverted}`,
73
+ );
74
+ logger.debug('coordination rejection — details', details);
75
+ return;
76
+ }
77
+
78
+ const reason = abloError?.message ? ` — ${abloError.message}` : '';
79
+ const code = abloError?.code ? ` (code: ${abloError.code})` : '';
80
+ const requestReference = abloError?.requestId
81
+ ? ` [request_id: ${abloError.requestId}]`
82
+ : '';
83
+ const reverted = ctx.enableOptimistic
84
+ ? ' The local change was reverted.'
85
+ : '';
86
+ logger.warn(
87
+ `Your ${transaction.type} to "${transaction.modelName}" was not saved${reason}${code}${requestReference}.${reverted}`,
88
+ );
89
+ logger.debug('write rejection — details', details);
90
+ } catch {
91
+ // Diagnostics must never interfere with rollback and promise settlement.
92
+ }
93
+ }