@prabhask5/stellar-engine 1.0.3

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 (138) hide show
  1. package/README.md +295 -0
  2. package/dist/actions/remoteChange.d.ts +79 -0
  3. package/dist/actions/remoteChange.d.ts.map +1 -0
  4. package/dist/actions/remoteChange.js +300 -0
  5. package/dist/actions/remoteChange.js.map +1 -0
  6. package/dist/auth/admin.d.ts +12 -0
  7. package/dist/auth/admin.d.ts.map +1 -0
  8. package/dist/auth/admin.js +23 -0
  9. package/dist/auth/admin.js.map +1 -0
  10. package/dist/auth/offlineCredentials.d.ts +41 -0
  11. package/dist/auth/offlineCredentials.d.ts.map +1 -0
  12. package/dist/auth/offlineCredentials.js +121 -0
  13. package/dist/auth/offlineCredentials.js.map +1 -0
  14. package/dist/auth/offlineLogin.d.ts +34 -0
  15. package/dist/auth/offlineLogin.d.ts.map +1 -0
  16. package/dist/auth/offlineLogin.js +75 -0
  17. package/dist/auth/offlineLogin.js.map +1 -0
  18. package/dist/auth/offlineSession.d.ts +22 -0
  19. package/dist/auth/offlineSession.d.ts.map +1 -0
  20. package/dist/auth/offlineSession.js +54 -0
  21. package/dist/auth/offlineSession.js.map +1 -0
  22. package/dist/auth/resolveAuthState.d.ts +24 -0
  23. package/dist/auth/resolveAuthState.d.ts.map +1 -0
  24. package/dist/auth/resolveAuthState.js +69 -0
  25. package/dist/auth/resolveAuthState.js.map +1 -0
  26. package/dist/config.d.ts +53 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +55 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/conflicts.d.ts +70 -0
  31. package/dist/conflicts.d.ts.map +1 -0
  32. package/dist/conflicts.js +321 -0
  33. package/dist/conflicts.js.map +1 -0
  34. package/dist/data.d.ts +77 -0
  35. package/dist/data.d.ts.map +1 -0
  36. package/dist/data.js +360 -0
  37. package/dist/data.js.map +1 -0
  38. package/dist/database.d.ts +31 -0
  39. package/dist/database.d.ts.map +1 -0
  40. package/dist/database.js +51 -0
  41. package/dist/database.js.map +1 -0
  42. package/dist/debug.d.ts +11 -0
  43. package/dist/debug.d.ts.map +1 -0
  44. package/dist/debug.js +48 -0
  45. package/dist/debug.js.map +1 -0
  46. package/dist/deviceId.d.ts +16 -0
  47. package/dist/deviceId.d.ts.map +1 -0
  48. package/dist/deviceId.js +48 -0
  49. package/dist/deviceId.js.map +1 -0
  50. package/dist/engine.d.ts +14 -0
  51. package/dist/engine.d.ts.map +1 -0
  52. package/dist/engine.js +1903 -0
  53. package/dist/engine.js.map +1 -0
  54. package/dist/entries/actions.d.ts +2 -0
  55. package/dist/entries/actions.d.ts.map +1 -0
  56. package/dist/entries/actions.js +3 -0
  57. package/dist/entries/actions.js.map +1 -0
  58. package/dist/entries/auth.d.ts +7 -0
  59. package/dist/entries/auth.d.ts.map +1 -0
  60. package/dist/entries/auth.js +6 -0
  61. package/dist/entries/auth.js.map +1 -0
  62. package/dist/entries/config.d.ts +3 -0
  63. package/dist/entries/config.d.ts.map +1 -0
  64. package/dist/entries/config.js +3 -0
  65. package/dist/entries/config.js.map +1 -0
  66. package/dist/entries/stores.d.ts +9 -0
  67. package/dist/entries/stores.d.ts.map +1 -0
  68. package/dist/entries/stores.js +9 -0
  69. package/dist/entries/stores.js.map +1 -0
  70. package/dist/entries/types.d.ts +11 -0
  71. package/dist/entries/types.d.ts.map +1 -0
  72. package/dist/entries/types.js +2 -0
  73. package/dist/entries/types.js.map +1 -0
  74. package/dist/entries/utils.d.ts +3 -0
  75. package/dist/entries/utils.d.ts.map +1 -0
  76. package/dist/entries/utils.js +4 -0
  77. package/dist/entries/utils.js.map +1 -0
  78. package/dist/index.d.ts +32 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +39 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/operations.d.ts +73 -0
  83. package/dist/operations.d.ts.map +1 -0
  84. package/dist/operations.js +227 -0
  85. package/dist/operations.js.map +1 -0
  86. package/dist/queue.d.ts +32 -0
  87. package/dist/queue.d.ts.map +1 -0
  88. package/dist/queue.js +377 -0
  89. package/dist/queue.js.map +1 -0
  90. package/dist/realtime.d.ts +57 -0
  91. package/dist/realtime.d.ts.map +1 -0
  92. package/dist/realtime.js +491 -0
  93. package/dist/realtime.js.map +1 -0
  94. package/dist/reconnectHandler.d.ts +16 -0
  95. package/dist/reconnectHandler.d.ts.map +1 -0
  96. package/dist/reconnectHandler.js +21 -0
  97. package/dist/reconnectHandler.js.map +1 -0
  98. package/dist/runtime/runtimeConfig.d.ts +27 -0
  99. package/dist/runtime/runtimeConfig.d.ts.map +1 -0
  100. package/dist/runtime/runtimeConfig.js +133 -0
  101. package/dist/runtime/runtimeConfig.js.map +1 -0
  102. package/dist/stores/authState.d.ts +57 -0
  103. package/dist/stores/authState.d.ts.map +1 -0
  104. package/dist/stores/authState.js +154 -0
  105. package/dist/stores/authState.js.map +1 -0
  106. package/dist/stores/network.d.ts +9 -0
  107. package/dist/stores/network.d.ts.map +1 -0
  108. package/dist/stores/network.js +97 -0
  109. package/dist/stores/network.js.map +1 -0
  110. package/dist/stores/remoteChanges.d.ts +142 -0
  111. package/dist/stores/remoteChanges.d.ts.map +1 -0
  112. package/dist/stores/remoteChanges.js +353 -0
  113. package/dist/stores/remoteChanges.js.map +1 -0
  114. package/dist/stores/sync.d.ts +35 -0
  115. package/dist/stores/sync.d.ts.map +1 -0
  116. package/dist/stores/sync.js +115 -0
  117. package/dist/stores/sync.js.map +1 -0
  118. package/dist/supabase/auth.d.ts +60 -0
  119. package/dist/supabase/auth.d.ts.map +1 -0
  120. package/dist/supabase/auth.js +298 -0
  121. package/dist/supabase/auth.js.map +1 -0
  122. package/dist/supabase/client.d.ts +15 -0
  123. package/dist/supabase/client.d.ts.map +1 -0
  124. package/dist/supabase/client.js +149 -0
  125. package/dist/supabase/client.js.map +1 -0
  126. package/dist/supabase/validate.d.ts +11 -0
  127. package/dist/supabase/validate.d.ts.map +1 -0
  128. package/dist/supabase/validate.js +38 -0
  129. package/dist/supabase/validate.js.map +1 -0
  130. package/dist/types.d.ts +78 -0
  131. package/dist/types.d.ts.map +1 -0
  132. package/dist/types.js +16 -0
  133. package/dist/types.js.map +1 -0
  134. package/dist/utils.d.ts +24 -0
  135. package/dist/utils.d.ts.map +1 -0
  136. package/dist/utils.js +56 -0
  137. package/dist/utils.js.map +1 -0
  138. package/package.json +84 -0
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ import { _setDebugPrefix } from './debug';
2
+ import { _setDeviceIdPrefix } from './deviceId';
3
+ import { _setClientPrefix } from './supabase/client';
4
+ import { _setConfigPrefix } from './runtime/runtimeConfig';
5
+ import { createDatabase, _setManagedDb } from './database';
6
+ let engineConfig = null;
7
+ export function initEngine(config) {
8
+ engineConfig = config;
9
+ // Propagate prefix to all internal modules
10
+ if (config.prefix) {
11
+ _setDebugPrefix(config.prefix);
12
+ _setDeviceIdPrefix(config.prefix);
13
+ _setClientPrefix(config.prefix);
14
+ _setConfigPrefix(config.prefix);
15
+ }
16
+ // Handle database creation
17
+ if (config.database) {
18
+ const db = createDatabase(config.database);
19
+ // Store on config for backward compat (engine.ts reads config.db)
20
+ config.db = db;
21
+ }
22
+ else if (config.db) {
23
+ // Backward compat: use provided Dexie instance
24
+ _setManagedDb(config.db);
25
+ }
26
+ }
27
+ export function getEngineConfig() {
28
+ if (!engineConfig) {
29
+ throw new Error('Sync engine not initialized. Call initEngine() first.');
30
+ }
31
+ return engineConfig;
32
+ }
33
+ /**
34
+ * Get the Supabase-to-Dexie table mapping derived from config.
35
+ */
36
+ export function getTableMap() {
37
+ const config = getEngineConfig();
38
+ const map = {};
39
+ for (const table of config.tables) {
40
+ map[table.supabaseName] = table.dexieTable;
41
+ }
42
+ return map;
43
+ }
44
+ /**
45
+ * Get columns for a specific Supabase table from config.
46
+ */
47
+ export function getTableColumns(supabaseName) {
48
+ const config = getEngineConfig();
49
+ const table = config.tables.find(t => t.supabaseName === supabaseName);
50
+ if (!table) {
51
+ throw new Error(`Table ${supabaseName} not found in engine config`);
52
+ }
53
+ return table.columns;
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AA8ChF,IAAI,YAAY,GAA4B,IAAI,CAAC;AAEjD,MAAM,UAAU,UAAU,CAAC,MAAwB;IACjD,YAAY,GAAG,MAAM,CAAC;IAEtB,2CAA2C;IAC3C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/B,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAClC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,2BAA2B;IAC3B,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC3C,kEAAkE;QACjE,MAAwB,CAAC,EAAE,GAAG,EAAE,CAAC;IACpC,CAAC;SAAM,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACrB,+CAA+C;QAC/C,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAGD;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,YAAY,CAAC,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,SAAS,YAAY,6BAA6B,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC,OAAO,CAAC;AACvB,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Conflict Resolution Engine
3
+ *
4
+ * Implements three-tier conflict resolution for multi-device sync:
5
+ *
6
+ * Tier 1: Non-overlapping (different entities) → AUTO-MERGE
7
+ * Tier 2: Different fields → AUTO-MERGE FIELDS
8
+ * Tier 3: Same field → Apply resolution strategy
9
+ *
10
+ * Resolution strategies for Tier 3:
11
+ * - Numeric fields with pending increments: Merge (sum deltas)
12
+ * - Delete operations: Delete wins over edits
13
+ * - All other cases: Last-write-wins (with deviceId tiebreaker)
14
+ */
15
+ import type { SyncOperationItem } from './types';
16
+ import type { ConflictHistoryEntry } from './types';
17
+ export type { ConflictHistoryEntry };
18
+ /**
19
+ * Conflict resolution result for a single field
20
+ */
21
+ export interface FieldConflictResolution {
22
+ field: string;
23
+ localValue: unknown;
24
+ remoteValue: unknown;
25
+ resolvedValue: unknown;
26
+ winner: 'local' | 'remote' | 'merged';
27
+ strategy: 'last_write' | 'numeric_merge' | 'delete_wins' | 'local_pending';
28
+ }
29
+ /**
30
+ * Full conflict resolution result for an entity
31
+ */
32
+ export interface ConflictResolution {
33
+ entityId: string;
34
+ entityType: string;
35
+ localUpdatedAt: string;
36
+ remoteUpdatedAt: string;
37
+ fieldResolutions: FieldConflictResolution[];
38
+ mergedEntity: Record<string, unknown>;
39
+ hasConflicts: boolean;
40
+ timestamp: string;
41
+ }
42
+ /**
43
+ * Resolve conflicts between local and remote entity states.
44
+ *
45
+ * @param entityType The type of entity (table name)
46
+ * @param entityId The entity's ID
47
+ * @param local The local entity state (may be null if entity doesn't exist locally)
48
+ * @param remote The remote entity state
49
+ * @param pendingOps Pending operations for this entity from the sync queue
50
+ * @returns The merged entity with conflict resolution applied
51
+ */
52
+ export declare function resolveConflicts(entityType: string, entityId: string, local: Record<string, unknown> | null, remote: Record<string, unknown>, pendingOps: SyncOperationItem[]): Promise<ConflictResolution>;
53
+ /**
54
+ * Store conflict resolution history for review/undo.
55
+ *
56
+ * @param resolution The conflict resolution result
57
+ */
58
+ export declare function storeConflictHistory(resolution: ConflictResolution): Promise<void>;
59
+ /**
60
+ * Get pending operations for a specific entity from the sync queue.
61
+ *
62
+ * @param entityId The entity ID to check
63
+ * @returns Array of pending operations for this entity
64
+ */
65
+ export declare function getPendingOpsForEntity(entityId: string): Promise<SyncOperationItem[]>;
66
+ /**
67
+ * Clean up old conflict history entries (older than 30 days).
68
+ */
69
+ export declare function cleanupConflictHistory(): Promise<number>;
70
+ //# sourceMappingURL=conflicts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAGpD,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACtC,QAAQ,EAAE,YAAY,GAAG,eAAe,GAAG,aAAa,GAAG,eAAe,CAAC;CAC5E;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,uBAAuB,EAAE,CAAC;IAC5C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAoBD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,EAAE,iBAAiB,EAAE,GAC9B,OAAO,CAAC,kBAAkB,CAAC,CA4K7B;AA8FD;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBxF;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAG3F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAiB9D"}
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Conflict Resolution Engine
3
+ *
4
+ * Implements three-tier conflict resolution for multi-device sync:
5
+ *
6
+ * Tier 1: Non-overlapping (different entities) → AUTO-MERGE
7
+ * Tier 2: Different fields → AUTO-MERGE FIELDS
8
+ * Tier 3: Same field → Apply resolution strategy
9
+ *
10
+ * Resolution strategies for Tier 3:
11
+ * - Numeric fields with pending increments: Merge (sum deltas)
12
+ * - Delete operations: Delete wins over edits
13
+ * - All other cases: Last-write-wins (with deviceId tiebreaker)
14
+ */
15
+ import { debugLog, debugError } from './debug';
16
+ import { getEngineConfig } from './config';
17
+ import { getDeviceId } from './deviceId';
18
+ /**
19
+ * Get excluded fields for a given entity type.
20
+ * Combines the default excluded fields with any per-table excludeFromConflict config.
21
+ */
22
+ function getExcludedFields(entityType) {
23
+ const defaultExcluded = new Set(['id', 'user_id', 'created_at', '_version']);
24
+ const tableConfig = getEngineConfig().tables.find(t => t.supabaseName === entityType);
25
+ return new Set([...defaultExcluded, ...(tableConfig?.excludeFromConflict || [])]);
26
+ }
27
+ /**
28
+ * Get numeric merge fields for a given entity type from per-table config.
29
+ */
30
+ function getNumericMergeFields(entityType) {
31
+ const tableConfig = getEngineConfig().tables.find(t => t.supabaseName === entityType);
32
+ return new Set(tableConfig?.numericMergeFields || []);
33
+ }
34
+ /**
35
+ * Resolve conflicts between local and remote entity states.
36
+ *
37
+ * @param entityType The type of entity (table name)
38
+ * @param entityId The entity's ID
39
+ * @param local The local entity state (may be null if entity doesn't exist locally)
40
+ * @param remote The remote entity state
41
+ * @param pendingOps Pending operations for this entity from the sync queue
42
+ * @returns The merged entity with conflict resolution applied
43
+ */
44
+ export async function resolveConflicts(entityType, entityId, local, remote, pendingOps) {
45
+ const timestamp = new Date().toISOString();
46
+ const fieldResolutions = [];
47
+ const deviceId = getDeviceId();
48
+ // If no local entity, remote wins entirely (no conflict)
49
+ if (!local) {
50
+ return {
51
+ entityId,
52
+ entityType,
53
+ localUpdatedAt: '',
54
+ remoteUpdatedAt: remote.updated_at,
55
+ fieldResolutions: [],
56
+ mergedEntity: { ...remote },
57
+ hasConflicts: false,
58
+ timestamp
59
+ };
60
+ }
61
+ const localUpdatedAt = local.updated_at;
62
+ const remoteUpdatedAt = remote.updated_at;
63
+ // Start with remote as base (since it's newer if we're processing it)
64
+ const mergedEntity = { ...remote };
65
+ // Get all unique fields from both entities
66
+ const allFields = new Set([...Object.keys(local), ...Object.keys(remote)]);
67
+ // Check for pending operations on specific fields
68
+ const pendingFieldOps = new Map();
69
+ for (const op of pendingOps) {
70
+ if (op.field) {
71
+ const existing = pendingFieldOps.get(op.field) || [];
72
+ existing.push(op);
73
+ pendingFieldOps.set(op.field, existing);
74
+ }
75
+ else if (op.operationType === 'set' && typeof op.value === 'object' && op.value !== null) {
76
+ // Multi-field set operation - extract fields
77
+ for (const field of Object.keys(op.value)) {
78
+ const existing = pendingFieldOps.get(field) || [];
79
+ existing.push(op);
80
+ pendingFieldOps.set(field, existing);
81
+ }
82
+ }
83
+ }
84
+ // Check if there's a pending delete
85
+ const hasPendingDelete = pendingOps.some((op) => op.operationType === 'delete');
86
+ // If there's a pending delete locally, local delete wins
87
+ if (hasPendingDelete && !remote.deleted) {
88
+ mergedEntity.deleted = true;
89
+ fieldResolutions.push({
90
+ field: 'deleted',
91
+ localValue: true,
92
+ remoteValue: remote.deleted,
93
+ resolvedValue: true,
94
+ winner: 'local',
95
+ strategy: 'local_pending'
96
+ });
97
+ }
98
+ // If remote is deleted but local has pending edits, delete still wins
99
+ // (This prevents resurrection of deleted entities)
100
+ if (remote.deleted && !hasPendingDelete) {
101
+ // Remote delete wins - entity should stay deleted
102
+ return {
103
+ entityId,
104
+ entityType,
105
+ localUpdatedAt,
106
+ remoteUpdatedAt,
107
+ fieldResolutions: [
108
+ {
109
+ field: 'deleted',
110
+ localValue: local.deleted,
111
+ remoteValue: true,
112
+ resolvedValue: true,
113
+ winner: 'remote',
114
+ strategy: 'delete_wins'
115
+ }
116
+ ],
117
+ mergedEntity: { ...remote },
118
+ hasConflicts: true,
119
+ timestamp
120
+ };
121
+ }
122
+ // Get config-driven field sets for this entity type
123
+ const excludedFields = getExcludedFields(entityType);
124
+ const numericMergeFields = getNumericMergeFields(entityType);
125
+ // Process each field
126
+ for (const field of allFields) {
127
+ if (excludedFields.has(field))
128
+ continue;
129
+ if (field === 'deleted' && hasPendingDelete)
130
+ continue; // Already handled
131
+ const localValue = local[field];
132
+ const remoteValue = remote[field];
133
+ // If values are equal, no conflict
134
+ if (valuesEqual(localValue, remoteValue)) {
135
+ continue;
136
+ }
137
+ // Check for pending operations on this field
138
+ const fieldOps = pendingFieldOps.get(field) || [];
139
+ const hasPendingOps = fieldOps.length > 0;
140
+ // Determine resolution strategy
141
+ let resolution;
142
+ if (hasPendingOps) {
143
+ // Tier 3a: Field has pending local operations - local wins
144
+ resolution = {
145
+ field,
146
+ localValue,
147
+ remoteValue,
148
+ resolvedValue: localValue,
149
+ winner: 'local',
150
+ strategy: 'local_pending'
151
+ };
152
+ mergedEntity[field] = localValue;
153
+ }
154
+ else if (numericMergeFields.has(field) && canNumericMerge(local, remote, field)) {
155
+ // Tier 3b: Numeric field that could theoretically be merged
156
+ // For now, use last-write-wins since we only have final values, not operation deltas
157
+ // True numeric merge (e.g., +50 + +30 = +80) would require an operation inbox system
158
+ resolution = resolveByTimestamp(field, local, remote, localUpdatedAt, remoteUpdatedAt, deviceId);
159
+ mergedEntity[field] = resolution.resolvedValue;
160
+ }
161
+ else {
162
+ // Tier 3c: Last-write-wins with timestamp comparison
163
+ resolution = resolveByTimestamp(field, local, remote, localUpdatedAt, remoteUpdatedAt, deviceId);
164
+ mergedEntity[field] = resolution.resolvedValue;
165
+ }
166
+ fieldResolutions.push(resolution);
167
+ }
168
+ // If there were field-level resolutions, we need to update the version
169
+ if (fieldResolutions.length > 0) {
170
+ const localVersion = typeof local._version === 'number' ? local._version : 1;
171
+ const remoteVersion = typeof remote._version === 'number' ? remote._version : 1;
172
+ mergedEntity._version = Math.max(localVersion, remoteVersion) + 1;
173
+ }
174
+ // Update updated_at to the later of the two
175
+ if (localUpdatedAt > remoteUpdatedAt) {
176
+ mergedEntity.updated_at = localUpdatedAt;
177
+ }
178
+ return {
179
+ entityId,
180
+ entityType,
181
+ localUpdatedAt,
182
+ remoteUpdatedAt,
183
+ fieldResolutions,
184
+ mergedEntity,
185
+ hasConflicts: fieldResolutions.length > 0,
186
+ timestamp
187
+ };
188
+ }
189
+ /**
190
+ * Resolve a field conflict using last-write-wins strategy.
191
+ * Uses deviceId as deterministic tiebreaker when timestamps are equal.
192
+ */
193
+ function resolveByTimestamp(field, local, remote, localUpdatedAt, remoteUpdatedAt, localDeviceId) {
194
+ const localValue = local[field];
195
+ const remoteValue = remote[field];
196
+ // Compare timestamps
197
+ const localTime = new Date(localUpdatedAt).getTime();
198
+ const remoteTime = new Date(remoteUpdatedAt).getTime();
199
+ let winner;
200
+ let resolvedValue;
201
+ if (localTime > remoteTime) {
202
+ winner = 'local';
203
+ resolvedValue = localValue;
204
+ }
205
+ else if (remoteTime > localTime) {
206
+ winner = 'remote';
207
+ resolvedValue = remoteValue;
208
+ }
209
+ else {
210
+ // Timestamps are equal - use deviceId as deterministic tiebreaker
211
+ // Lower deviceId wins (arbitrary but consistent across all devices)
212
+ const remoteDeviceId = remote.device_id || '';
213
+ if (remoteDeviceId && localDeviceId < remoteDeviceId) {
214
+ winner = 'local';
215
+ resolvedValue = localValue;
216
+ }
217
+ else if (remoteDeviceId && localDeviceId > remoteDeviceId) {
218
+ winner = 'remote';
219
+ resolvedValue = remoteValue;
220
+ }
221
+ else {
222
+ // Same device or no remote device_id - local wins (it's the more recent action)
223
+ winner = 'local';
224
+ resolvedValue = localValue;
225
+ }
226
+ }
227
+ return {
228
+ field,
229
+ localValue,
230
+ remoteValue,
231
+ resolvedValue,
232
+ winner,
233
+ strategy: 'last_write'
234
+ };
235
+ }
236
+ /**
237
+ * Check if two values are equal (deep comparison for objects/arrays).
238
+ */
239
+ function valuesEqual(a, b) {
240
+ if (a === b)
241
+ return true;
242
+ if (a === null || b === null)
243
+ return a === b;
244
+ if (typeof a !== typeof b)
245
+ return false;
246
+ if (Array.isArray(a) && Array.isArray(b)) {
247
+ if (a.length !== b.length)
248
+ return false;
249
+ return a.every((val, i) => valuesEqual(val, b[i]));
250
+ }
251
+ if (typeof a === 'object' && typeof b === 'object') {
252
+ const aKeys = Object.keys(a);
253
+ const bKeys = Object.keys(b);
254
+ if (aKeys.length !== bKeys.length)
255
+ return false;
256
+ return aKeys.every((key) => valuesEqual(a[key], b[key]));
257
+ }
258
+ return false;
259
+ }
260
+ /**
261
+ * Check if a numeric field can be merged (both sides have numeric values).
262
+ */
263
+ function canNumericMerge(local, remote, field) {
264
+ return typeof local[field] === 'number' && typeof remote[field] === 'number';
265
+ }
266
+ /**
267
+ * Store conflict resolution history for review/undo.
268
+ *
269
+ * @param resolution The conflict resolution result
270
+ */
271
+ export async function storeConflictHistory(resolution) {
272
+ if (!resolution.hasConflicts)
273
+ return;
274
+ try {
275
+ const entries = resolution.fieldResolutions.map((fr) => ({
276
+ entityId: resolution.entityId,
277
+ entityType: resolution.entityType,
278
+ field: fr.field,
279
+ localValue: fr.localValue,
280
+ remoteValue: fr.remoteValue,
281
+ resolvedValue: fr.resolvedValue,
282
+ winner: fr.winner,
283
+ strategy: fr.strategy,
284
+ timestamp: resolution.timestamp
285
+ }));
286
+ await getEngineConfig().db.table('conflictHistory').bulkAdd(entries);
287
+ }
288
+ catch (error) {
289
+ debugError('[Conflict] Failed to store conflict history:', error);
290
+ }
291
+ }
292
+ /**
293
+ * Get pending operations for a specific entity from the sync queue.
294
+ *
295
+ * @param entityId The entity ID to check
296
+ * @returns Array of pending operations for this entity
297
+ */
298
+ export async function getPendingOpsForEntity(entityId) {
299
+ const allPending = await getEngineConfig().db.table('syncQueue').where('entityId').equals(entityId).toArray();
300
+ return allPending;
301
+ }
302
+ /**
303
+ * Clean up old conflict history entries (older than 30 days).
304
+ */
305
+ export async function cleanupConflictHistory() {
306
+ const cutoffDate = new Date();
307
+ cutoffDate.setDate(cutoffDate.getDate() - 30);
308
+ const cutoffStr = cutoffDate.toISOString();
309
+ try {
310
+ const count = await getEngineConfig().db.table('conflictHistory').filter((entry) => entry.timestamp < cutoffStr).delete();
311
+ if (count > 0) {
312
+ debugLog(`[Conflict] Cleaned up ${count} old conflict history entries`);
313
+ }
314
+ return count;
315
+ }
316
+ catch (error) {
317
+ debugError('[Conflict] Failed to cleanup conflict history:', error);
318
+ return 0;
319
+ }
320
+ }
321
+ //# sourceMappingURL=conflicts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflicts.js","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"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"}
package/dist/data.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Generic CRUD and Query Operations
3
+ *
4
+ * Replaces repository infrastructure boilerplate.
5
+ * Uses Supabase table names as the API surface, internally resolves to Dexie table names.
6
+ */
7
+ /**
8
+ * Create a new entity. Auto: transaction + queue + markModified + schedulePush.
9
+ * Caller provides all fields including id, timestamps, etc.
10
+ */
11
+ export declare function engineCreate(table: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
12
+ /**
13
+ * Update an entity's fields. Auto-sets updated_at, queues sync, marks modified.
14
+ */
15
+ export declare function engineUpdate(table: string, id: string, fields: Record<string, unknown>): Promise<Record<string, unknown> | undefined>;
16
+ /**
17
+ * Soft-delete an entity. Sets deleted=true, queues delete op.
18
+ */
19
+ export declare function engineDelete(table: string, id: string): Promise<void>;
20
+ export type BatchOperation = {
21
+ type: 'create';
22
+ table: string;
23
+ data: Record<string, unknown>;
24
+ } | {
25
+ type: 'update';
26
+ table: string;
27
+ id: string;
28
+ fields: Record<string, unknown>;
29
+ } | {
30
+ type: 'delete';
31
+ table: string;
32
+ id: string;
33
+ };
34
+ /**
35
+ * Execute multiple write operations in a single atomic transaction.
36
+ * All ops succeed or all roll back.
37
+ */
38
+ export declare function engineBatchWrite(operations: BatchOperation[]): Promise<void>;
39
+ /**
40
+ * Increment a numeric field on an entity, preserving increment intent for conflict resolution.
41
+ * Uses increment operationType in the sync queue so multi-device increments are additive.
42
+ * Optionally sets additional fields (e.g., completed, updated_at) alongside the increment.
43
+ */
44
+ export declare function engineIncrement(table: string, id: string, field: string, amount: number, additionalFields?: Record<string, unknown>): Promise<Record<string, unknown> | undefined>;
45
+ /**
46
+ * Get a single entity by ID. Optional remote fallback if not found locally.
47
+ */
48
+ export declare function engineGet(table: string, id: string, opts?: {
49
+ remoteFallback?: boolean;
50
+ }): Promise<Record<string, unknown> | null>;
51
+ /**
52
+ * Get all entities from a table. Optional ordering and remote fallback.
53
+ */
54
+ export declare function engineGetAll(table: string, opts?: {
55
+ orderBy?: string;
56
+ remoteFallback?: boolean;
57
+ }): Promise<Record<string, unknown>[]>;
58
+ /**
59
+ * Query entities by index value (WHERE index = value).
60
+ */
61
+ export declare function engineQuery(table: string, index: string, value: unknown, opts?: {
62
+ remoteFallback?: boolean;
63
+ }): Promise<Record<string, unknown>[]>;
64
+ /**
65
+ * Range query (WHERE index BETWEEN lower AND upper).
66
+ */
67
+ export declare function engineQueryRange(table: string, index: string, lower: unknown, upper: unknown, opts?: {
68
+ remoteFallback?: boolean;
69
+ }): Promise<Record<string, unknown>[]>;
70
+ /**
71
+ * Singleton get-or-create with optional remote check.
72
+ * Used for patterns like focus_settings where one record per user exists.
73
+ */
74
+ export declare function engineGetOrCreate(table: string, index: string, value: unknown, defaults: Record<string, unknown>, opts?: {
75
+ checkRemote?: boolean;
76
+ }): Promise<Record<string, unknown>>;
77
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../src/data.ts"],"names":[],"mappings":"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"}