@abloatai/ablo 0.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 (278) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +12 -0
  4. package/README.md +230 -0
  5. package/dist/BaseSyncedStore.d.ts +709 -0
  6. package/dist/BaseSyncedStore.js +1843 -0
  7. package/dist/Database.d.ts +344 -0
  8. package/dist/Database.js +1259 -0
  9. package/dist/LazyReferenceCollection.d.ts +181 -0
  10. package/dist/LazyReferenceCollection.js +460 -0
  11. package/dist/Model.d.ts +339 -0
  12. package/dist/Model.js +715 -0
  13. package/dist/ModelRegistry.d.ts +200 -0
  14. package/dist/ModelRegistry.js +535 -0
  15. package/dist/NetworkMonitor.d.ts +27 -0
  16. package/dist/NetworkMonitor.js +73 -0
  17. package/dist/ObjectPool.d.ts +202 -0
  18. package/dist/ObjectPool.js +1106 -0
  19. package/dist/SyncClient.d.ts +489 -0
  20. package/dist/SyncClient.js +1555 -0
  21. package/dist/SyncEngineContext.d.ts +46 -0
  22. package/dist/SyncEngineContext.js +74 -0
  23. package/dist/adapters/alwaysOnline.d.ts +16 -0
  24. package/dist/adapters/alwaysOnline.js +19 -0
  25. package/dist/adapters/inMemoryStorage.d.ts +30 -0
  26. package/dist/adapters/inMemoryStorage.js +94 -0
  27. package/dist/agent/Agent.d.ts +358 -0
  28. package/dist/agent/Agent.js +500 -0
  29. package/dist/agent/index.d.ts +115 -0
  30. package/dist/agent/index.js +128 -0
  31. package/dist/agent/session.d.ts +90 -0
  32. package/dist/agent/session.js +156 -0
  33. package/dist/agent/types.d.ts +73 -0
  34. package/dist/agent/types.js +10 -0
  35. package/dist/ai-sdk/coordination-context.d.ts +51 -0
  36. package/dist/ai-sdk/coordination-context.js +107 -0
  37. package/dist/ai-sdk/index.d.ts +68 -0
  38. package/dist/ai-sdk/index.js +68 -0
  39. package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
  40. package/dist/ai-sdk/intent-broadcast.js +72 -0
  41. package/dist/ai-sdk/wrap.d.ts +67 -0
  42. package/dist/ai-sdk/wrap.js +45 -0
  43. package/dist/api/index.d.ts +10 -0
  44. package/dist/api/index.js +9 -0
  45. package/dist/auth/index.d.ts +137 -0
  46. package/dist/auth/index.js +246 -0
  47. package/dist/client/Ablo.d.ts +835 -0
  48. package/dist/client/Ablo.js +1440 -0
  49. package/dist/client/ApiClient.d.ts +200 -0
  50. package/dist/client/ApiClient.js +659 -0
  51. package/dist/client/auth.d.ts +79 -0
  52. package/dist/client/auth.js +81 -0
  53. package/dist/client/createInternalComponents.d.ts +44 -0
  54. package/dist/client/createInternalComponents.js +88 -0
  55. package/dist/client/createModelProxy.d.ts +152 -0
  56. package/dist/client/createModelProxy.js +199 -0
  57. package/dist/client/identity.d.ts +63 -0
  58. package/dist/client/identity.js +156 -0
  59. package/dist/client/index.d.ts +36 -0
  60. package/dist/client/index.js +33 -0
  61. package/dist/client/persistence.d.ts +7 -0
  62. package/dist/client/persistence.js +11 -0
  63. package/dist/client/validateAbloOptions.d.ts +42 -0
  64. package/dist/client/validateAbloOptions.js +43 -0
  65. package/dist/config/index.d.ts +10 -0
  66. package/dist/config/index.js +12 -0
  67. package/dist/context.d.ts +27 -0
  68. package/dist/context.js +58 -0
  69. package/dist/core/DatabaseManager.d.ts +108 -0
  70. package/dist/core/DatabaseManager.js +361 -0
  71. package/dist/core/QueryProcessor.d.ts +77 -0
  72. package/dist/core/QueryProcessor.js +262 -0
  73. package/dist/core/QueryView.d.ts +64 -0
  74. package/dist/core/QueryView.js +219 -0
  75. package/dist/core/StoreManager.d.ts +131 -0
  76. package/dist/core/StoreManager.js +334 -0
  77. package/dist/core/ViewRegistry.d.ts +20 -0
  78. package/dist/core/ViewRegistry.js +55 -0
  79. package/dist/core/index.d.ts +34 -0
  80. package/dist/core/index.js +59 -0
  81. package/dist/core/openIDBWithTimeout.d.ts +27 -0
  82. package/dist/core/openIDBWithTimeout.js +63 -0
  83. package/dist/core/query-utils.d.ts +37 -0
  84. package/dist/core/query-utils.js +60 -0
  85. package/dist/errors.d.ts +235 -0
  86. package/dist/errors.js +243 -0
  87. package/dist/index.d.ts +41 -0
  88. package/dist/index.js +82 -0
  89. package/dist/interfaces/headless.d.ts +95 -0
  90. package/dist/interfaces/headless.js +41 -0
  91. package/dist/interfaces/index.d.ts +321 -0
  92. package/dist/interfaces/index.js +8 -0
  93. package/dist/mutators/RecordingTransaction.d.ts +36 -0
  94. package/dist/mutators/RecordingTransaction.js +216 -0
  95. package/dist/mutators/Transaction.d.ts +48 -0
  96. package/dist/mutators/Transaction.js +64 -0
  97. package/dist/mutators/UndoManager.d.ts +114 -0
  98. package/dist/mutators/UndoManager.js +143 -0
  99. package/dist/mutators/defineMutators.d.ts +55 -0
  100. package/dist/mutators/defineMutators.js +28 -0
  101. package/dist/policy/index.d.ts +19 -0
  102. package/dist/policy/index.js +18 -0
  103. package/dist/policy/types.d.ts +74 -0
  104. package/dist/policy/types.js +17 -0
  105. package/dist/principal.d.ts +44 -0
  106. package/dist/principal.js +49 -0
  107. package/dist/query/client.d.ts +43 -0
  108. package/dist/query/client.js +84 -0
  109. package/dist/query/index.d.ts +6 -0
  110. package/dist/query/index.js +5 -0
  111. package/dist/query/types.d.ts +143 -0
  112. package/dist/query/types.js +36 -0
  113. package/dist/react/AbloProvider.d.ts +205 -0
  114. package/dist/react/AbloProvider.js +398 -0
  115. package/dist/react/ClientSideSuspense.d.ts +36 -0
  116. package/dist/react/ClientSideSuspense.js +17 -0
  117. package/dist/react/DefaultFallback.d.ts +24 -0
  118. package/dist/react/DefaultFallback.js +43 -0
  119. package/dist/react/SyncGroupProvider.d.ts +19 -0
  120. package/dist/react/SyncGroupProvider.js +44 -0
  121. package/dist/react/context.d.ts +161 -0
  122. package/dist/react/context.js +35 -0
  123. package/dist/react/index.d.ts +64 -0
  124. package/dist/react/index.js +73 -0
  125. package/dist/react/internalContext.d.ts +35 -0
  126. package/dist/react/internalContext.js +3 -0
  127. package/dist/react/useAblo.d.ts +72 -0
  128. package/dist/react/useAblo.js +63 -0
  129. package/dist/react/useCurrentUserId.d.ts +21 -0
  130. package/dist/react/useCurrentUserId.js +33 -0
  131. package/dist/react/useErrorListener.d.ts +20 -0
  132. package/dist/react/useErrorListener.js +39 -0
  133. package/dist/react/useIntent.d.ts +29 -0
  134. package/dist/react/useIntent.js +42 -0
  135. package/dist/react/useMutate.d.ts +83 -0
  136. package/dist/react/useMutate.js +122 -0
  137. package/dist/react/useMutationFailureListener.d.ts +26 -0
  138. package/dist/react/useMutationFailureListener.js +38 -0
  139. package/dist/react/useMutators.d.ts +56 -0
  140. package/dist/react/useMutators.js +66 -0
  141. package/dist/react/usePresence.d.ts +32 -0
  142. package/dist/react/usePresence.js +41 -0
  143. package/dist/react/useQuery.d.ts +123 -0
  144. package/dist/react/useQuery.js +145 -0
  145. package/dist/react/useReactive.d.ts +35 -0
  146. package/dist/react/useReactive.js +111 -0
  147. package/dist/react/useReader.d.ts +69 -0
  148. package/dist/react/useReader.js +73 -0
  149. package/dist/react/useSyncStatus.d.ts +61 -0
  150. package/dist/react/useSyncStatus.js +76 -0
  151. package/dist/react/useUndoScope.d.ts +36 -0
  152. package/dist/react/useUndoScope.js +73 -0
  153. package/dist/realtime/index.d.ts +10 -0
  154. package/dist/realtime/index.js +9 -0
  155. package/dist/schema/field.d.ts +134 -0
  156. package/dist/schema/field.js +264 -0
  157. package/dist/schema/index.d.ts +29 -0
  158. package/dist/schema/index.js +38 -0
  159. package/dist/schema/model.d.ts +326 -0
  160. package/dist/schema/model.js +89 -0
  161. package/dist/schema/queries.d.ts +203 -0
  162. package/dist/schema/queries.js +145 -0
  163. package/dist/schema/relation.d.ts +172 -0
  164. package/dist/schema/relation.js +104 -0
  165. package/dist/schema/schema.d.ts +259 -0
  166. package/dist/schema/schema.js +188 -0
  167. package/dist/schema/sugar.d.ts +129 -0
  168. package/dist/schema/sugar.js +94 -0
  169. package/dist/source/index.d.ts +423 -0
  170. package/dist/source/index.js +320 -0
  171. package/dist/source/pushQueue.d.ts +112 -0
  172. package/dist/source/pushQueue.js +249 -0
  173. package/dist/stores/ObjectStore.d.ts +103 -0
  174. package/dist/stores/ObjectStore.js +371 -0
  175. package/dist/stores/ObjectStoreContract.d.ts +39 -0
  176. package/dist/stores/ObjectStoreContract.js +1 -0
  177. package/dist/stores/SyncActionStore.d.ts +101 -0
  178. package/dist/stores/SyncActionStore.js +481 -0
  179. package/dist/sync/BootstrapHelper.d.ts +127 -0
  180. package/dist/sync/BootstrapHelper.js +434 -0
  181. package/dist/sync/ConnectionManager.d.ts +136 -0
  182. package/dist/sync/ConnectionManager.js +465 -0
  183. package/dist/sync/HydrationCoordinator.d.ts +137 -0
  184. package/dist/sync/HydrationCoordinator.js +468 -0
  185. package/dist/sync/NetworkProbe.d.ts +43 -0
  186. package/dist/sync/NetworkProbe.js +113 -0
  187. package/dist/sync/OfflineFlush.d.ts +9 -0
  188. package/dist/sync/OfflineFlush.js +22 -0
  189. package/dist/sync/OfflineTransactionStore.d.ts +37 -0
  190. package/dist/sync/OfflineTransactionStore.js +263 -0
  191. package/dist/sync/SyncWebSocket.d.ts +663 -0
  192. package/dist/sync/SyncWebSocket.js +1336 -0
  193. package/dist/sync/createIntentStream.d.ts +33 -0
  194. package/dist/sync/createIntentStream.js +243 -0
  195. package/dist/sync/createPresenceStream.d.ts +46 -0
  196. package/dist/sync/createPresenceStream.js +192 -0
  197. package/dist/sync/createSnapshot.d.ts +33 -0
  198. package/dist/sync/createSnapshot.js +124 -0
  199. package/dist/sync/participants.d.ts +114 -0
  200. package/dist/sync/participants.js +336 -0
  201. package/dist/sync/schemas.d.ts +79 -0
  202. package/dist/sync/schemas.js +78 -0
  203. package/dist/testing/fixtures/bootstrap.d.ts +45 -0
  204. package/dist/testing/fixtures/bootstrap.js +53 -0
  205. package/dist/testing/fixtures/deltas.d.ts +86 -0
  206. package/dist/testing/fixtures/deltas.js +139 -0
  207. package/dist/testing/fixtures/models.d.ts +82 -0
  208. package/dist/testing/fixtures/models.js +270 -0
  209. package/dist/testing/helpers/react-wrapper.d.ts +66 -0
  210. package/dist/testing/helpers/react-wrapper.js +64 -0
  211. package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
  212. package/dist/testing/helpers/sync-engine-harness.js +70 -0
  213. package/dist/testing/helpers/wait.d.ts +25 -0
  214. package/dist/testing/helpers/wait.js +44 -0
  215. package/dist/testing/index.d.ts +21 -0
  216. package/dist/testing/index.js +32 -0
  217. package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
  218. package/dist/testing/mocks/MockMutationExecutor.js +139 -0
  219. package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
  220. package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
  221. package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
  222. package/dist/testing/mocks/MockSyncContext.js +100 -0
  223. package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
  224. package/dist/testing/mocks/MockSyncStore.js +171 -0
  225. package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
  226. package/dist/testing/mocks/MockWebSocket.js +117 -0
  227. package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
  228. package/dist/transactions/OptimisticEchoTracker.js +104 -0
  229. package/dist/transactions/TransactionQueue.d.ts +499 -0
  230. package/dist/transactions/TransactionQueue.js +1895 -0
  231. package/dist/transactions/index.d.ts +16 -0
  232. package/dist/transactions/index.js +7 -0
  233. package/dist/transactions/mutation-error-handler.d.ts +5 -0
  234. package/dist/transactions/mutation-error-handler.js +39 -0
  235. package/dist/types/global.d.ts +107 -0
  236. package/dist/types/global.js +38 -0
  237. package/dist/types/index.d.ts +241 -0
  238. package/dist/types/index.js +70 -0
  239. package/dist/types/streams.d.ts +495 -0
  240. package/dist/types/streams.js +11 -0
  241. package/dist/utils/asyncIterator.d.ts +41 -0
  242. package/dist/utils/asyncIterator.js +142 -0
  243. package/dist/utils/duration.d.ts +28 -0
  244. package/dist/utils/duration.js +47 -0
  245. package/dist/utils/mobx-setup.d.ts +42 -0
  246. package/dist/utils/mobx-setup.js +381 -0
  247. package/docs/api-keys.md +24 -0
  248. package/docs/api.md +230 -0
  249. package/docs/audit.md +81 -0
  250. package/docs/capabilities.md +163 -0
  251. package/docs/client-behavior.md +202 -0
  252. package/docs/data-sources.md +214 -0
  253. package/docs/examples/agent-human.md +84 -0
  254. package/docs/examples/ai-sdk-tool.md +92 -0
  255. package/docs/examples/existing-python-backend.md +249 -0
  256. package/docs/examples/nextjs.md +88 -0
  257. package/docs/examples/server-agent.md +86 -0
  258. package/docs/guarantees.md +148 -0
  259. package/docs/index.md +97 -0
  260. package/docs/integration-guide.md +493 -0
  261. package/docs/interaction-model.md +140 -0
  262. package/docs/mcp/claude-code.md +43 -0
  263. package/docs/mcp/cursor.md +53 -0
  264. package/docs/mcp/windsurf.md +46 -0
  265. package/docs/mcp.md +59 -0
  266. package/docs/quickstart.md +152 -0
  267. package/docs/react.md +115 -0
  268. package/docs/roadmap.md +45 -0
  269. package/examples/README.md +54 -0
  270. package/examples/data-source/README.md +102 -0
  271. package/examples/data-source/ablo-driver.ts +89 -0
  272. package/examples/data-source/customer-server.ts +208 -0
  273. package/examples/data-source/run.ts +101 -0
  274. package/examples/data-source/schema.ts +25 -0
  275. package/examples/quickstart.ts +54 -0
  276. package/examples/tsconfig.json +16 -0
  277. package/llms.txt +143 -0
  278. package/package.json +147 -0
@@ -0,0 +1,489 @@
1
+ /**
2
+ * SyncClient - Mutation and offline queue manager
3
+ *
4
+ * Responsibilities:
5
+ * - Handle model mutations (create, update, delete, archive)
6
+ * - Manage offline mutation queue with persistence
7
+ * - Send mutations to server via API client
8
+ * - Handle conflict resolution for local changes
9
+ */
10
+ import { ObjectPool } from './ObjectPool.js';
11
+ import { Model } from './Model.js';
12
+ import { EventEmitter } from 'events';
13
+ import { TransactionQueue } from './transactions/TransactionQueue.js';
14
+ import { type OptimisticEchoMetrics } from './transactions/OptimisticEchoTracker.js';
15
+ import type { Database } from './Database.js';
16
+ import type { MutationOptions } from './interfaces/index.js';
17
+ interface SyncObserver {
18
+ onSync?: (event: SyncEvent) => void;
19
+ }
20
+ interface SyncEvent {
21
+ type: 'create' | 'update' | 'delete' | 'archive' | 'rollback';
22
+ modelType: string;
23
+ model?: Model;
24
+ modelId?: string;
25
+ transactionType?: string;
26
+ }
27
+ interface SyncState {
28
+ connectionState: 'connected' | 'disconnected' | 'connecting';
29
+ pendingMutations: number;
30
+ lastSyncAt?: Date;
31
+ error?: Error;
32
+ }
33
+ export interface RehydrationStats {
34
+ added: number;
35
+ updated: number;
36
+ removed: number;
37
+ skipped: number;
38
+ healed: number;
39
+ elapsedMs: number;
40
+ }
41
+ type MutationWriteOptions = Pick<MutationOptions, 'readAt' | 'onStale'>;
42
+ export declare class SyncClient extends EventEmitter {
43
+ private objectPool;
44
+ private database;
45
+ private get mutationExecutor();
46
+ private networkMonitor;
47
+ private transactionQueue;
48
+ private observers;
49
+ private userId;
50
+ private organizationId;
51
+ private pendingMutations;
52
+ /**
53
+ * Tracks transaction ids the client has optimistically applied but
54
+ * the server has not yet confirmed. The receive path consults it
55
+ * to recognize delta echoes of own mutations and suppress the
56
+ * (otherwise-redundant) pool mutation — the IDB write still runs
57
+ * because the delta is the authoritative version of the row.
58
+ *
59
+ * The receive-layer discriminator named in
60
+ * `apps/sync-server/docs/OPTIMISTIC_RECONCILIATION.md`. Without
61
+ * it, an optimistically-applied DELETE followed by a
62
+ * server-confirming CREATE echo resurrects the row for the window
63
+ * between the two confirmations (the chart-delete flicker).
64
+ *
65
+ * Bounded with FIFO eviction; observability via `getEchoMetrics()`.
66
+ */
67
+ private readonly echoTracker;
68
+ private connectionState;
69
+ private offlineSince?;
70
+ private maxRetries;
71
+ private isDisposed;
72
+ constructor(objectPool: ObjectPool, database: Database);
73
+ /**
74
+ * Setup network monitoring handlers
75
+ */
76
+ private setupNetworkMonitoring;
77
+ /**
78
+ * Handle transaction rollback. Two distinct shapes flow through this
79
+ * event:
80
+ *
81
+ * 1. **Server-rejected rollback** (`reason === 'permanent_error'`,
82
+ * `'max_retries_exhausted'`, `'conflict_server_wins'`) — the
83
+ * optimistic state is wrong, the row exists, restore previous
84
+ * state and notify the UI.
85
+ *
86
+ * 2. **Local-cancellation cleanup** (`reason === 'model_cancelled'`,
87
+ * `'cascade_parent_deleted'`) — the user deleted this model (or
88
+ * its parent), so a pending UPDATE on it gets cancelled. There's
89
+ * nothing to restore (the model is doomed) and no UI notification
90
+ * needed (the delete itself already triggered re-renders). Just
91
+ * discard the optimistic state silently.
92
+ *
93
+ * Treating both paths the same caused the deletion-flicker bug: every
94
+ * cancelled update on a multi-layer chart fired a per-model observer
95
+ * event and a `[SyncClient.rollback]` warn, producing N renders and N
96
+ * spam log lines for one user-initiated delete.
97
+ */
98
+ private setupTransactionRollbackHandling;
99
+ /**
100
+ * Forward reconciliation requests from TransactionQueue to the sync layer.
101
+ * When delta confirmation times out, TransactionQueue emits 'reconciliation:needed'
102
+ * instead of rolling back — following the Replicache/PowerSync pattern of never
103
+ * destroying optimistic state that the server may have committed.
104
+ */
105
+ private setupReconciliationForwarding;
106
+ /**
107
+ * LINEAR PATTERN: Persist unconfirmed transactions to IndexedDB.
108
+ * When delta confirmation retries exhaust, the transaction data is cached in IDB
109
+ * so it survives tab close. On next session, WebSocket reconnect + delta catch-up
110
+ * will deliver the missing deltas and naturally confirm the transaction.
111
+ */
112
+ private setupAwaitingTransactionPersistence;
113
+ /**
114
+ * Initialize sync client with authentication
115
+ */
116
+ initialize(userId: string, organizationId: string): Promise<void>;
117
+ /**
118
+ * Self-healing helper for individual model records.
119
+ *
120
+ * Two registry-driven repair passes run on every row hydrated from
121
+ * IDB or merged from a delta:
122
+ *
123
+ * 1. **Auto-fill** — for each `autoFill` rule the consumer's schema
124
+ * declares on this model, copy the corresponding identity value
125
+ * (`organizationId` / `userId`) onto the row when it's missing.
126
+ * Repairs rows from a past version that didn't write the field.
127
+ *
128
+ * 2. **Required-field gate** — if the row is missing any field listed
129
+ * in the model's `requiredFields`, return `null` so the caller
130
+ * skips this record. Used for FK columns whose absence renders the
131
+ * row unrecoverable (e.g. a SlideLayer with no slideId).
132
+ *
133
+ * The engine itself is product-neutral: model identity (which fields
134
+ * to back-fill, which absences are fatal) lives entirely in the
135
+ * consumer schema.
136
+ */
137
+ healModelRecord(modelType: string, data: Record<string, unknown>): {
138
+ data: Record<string, unknown>;
139
+ healed: boolean;
140
+ } | null;
141
+ /**
142
+ * Hydrate ObjectPool with data from Database
143
+ * Called after bootstrap is complete
144
+ */
145
+ hydrateFromDatabase(): Promise<void>;
146
+ /**
147
+ * Re-hydrate ObjectPool from IndexedDB when the pool already has data.
148
+ *
149
+ * Unlike hydrateFromDatabase() (which uses addBatch and skips existing IDs),
150
+ * this method properly:
151
+ * 1. Upserts models — updates existing models in-place, adds new ones
152
+ * 2. Removes ghosts — deletes models from the pool that no longer exist in IndexedDB
153
+ *
154
+ * Used by background bootstrap, network recovery, and server-triggered re-bootstrap.
155
+ */
156
+ rehydrateFromDatabase(): Promise<RehydrationStats>;
157
+ /**
158
+ * Mutate model optimistically and queue for server sync.
159
+ * IndexedDB is only updated when server confirms via delta packet.
160
+ *
161
+ * CRITICAL: Changes are captured BEFORE poolAction to prevent data loss.
162
+ * The captured changes are frozen and passed to queueMutation.
163
+ *
164
+ * @see src/sync-engine/types/TrackableModel.ts for change capture pattern
165
+ */
166
+ private mutate;
167
+ private pendingChangedTypes;
168
+ private markModelChanged;
169
+ /**
170
+ * Capture model changes immutably BEFORE any pool operations
171
+ * This prevents the fragile pattern of reading changes after state modification
172
+ */
173
+ private captureModelChanges;
174
+ /** Add new model (CREATE) - works offline */
175
+ add(model: Model, options?: MutationWriteOptions): void;
176
+ /** Update existing model (UPDATE) - works offline */
177
+ update(model: Model, options?: MutationWriteOptions): void;
178
+ /**
179
+ * Update existing model with pre-computed changes.
180
+ * Used by saveManyOptimized when incoming models have empty change-tracking
181
+ * (e.g. freshly constructed SpreadsheetCellModels from decomposeSpreadsheetDocument).
182
+ */
183
+ updateWithChanges(model: Model, changes?: Record<string, unknown>): void;
184
+ /** Expose the GraphQL client for atomic mutations (e.g., createSlideWithLayers).
185
+ * Used by SyncedStore for operations that bypass the transaction queue
186
+ * but still need optimistic pool updates at the sync layer. */
187
+ get gql(): import("./interfaces/index.js").MutationExecutor;
188
+ /** Delete model (DELETE) - works offline */
189
+ delete(model: Model, options?: MutationWriteOptions): void;
190
+ /**
191
+ * Clear all pending mutations for a specific model
192
+ * Called before deletion to prevent "layer not found" errors on the server
193
+ */
194
+ private clearPendingMutationsForModel;
195
+ /**
196
+ * Upload file and create attachment (UPLOAD operation)
197
+ * Uses Linear-style pattern with immediate URL generation
198
+ */
199
+ uploadFile(file: File, options: {
200
+ id: string;
201
+ attachableType: string;
202
+ attachableId: string;
203
+ metadata?: Record<string, unknown>;
204
+ }): Promise<Model | null>;
205
+ /**
206
+ * Batch upload files — single GraphQL call + parallel S3 PUTs.
207
+ *
208
+ * Returns the raw `Model[]` built by the object pool (typename is
209
+ * determined by the payload the server returns — currently always
210
+ * `Attachment`). The SDK has no knowledge of app-specific model classes,
211
+ * so it cannot honestly claim a narrower return type; consumers that
212
+ * need an `Attachment[]` project through their own typed accessor
213
+ * (e.g. `store.query.attachments.findMany({ where: { id: IN ids } })`)
214
+ * after the upload resolves.
215
+ */
216
+ batchUploadFiles(files: File[], options: {
217
+ ids: string[];
218
+ attachableType: string;
219
+ attachableId: string;
220
+ metadata?: Record<string, unknown>;
221
+ }): Promise<Model[]>;
222
+ /** Archive model (ARCHIVE) - works offline */
223
+ archive(model: Model): void;
224
+ /**
225
+ * Append a mutation and schedule its sync work.
226
+ *
227
+ * IDB persistence and the server push are deferred to a microtask so N
228
+ * pushes inside the same tick collapse into ONE IDB serialization + ONE
229
+ * process call. Without the deferral, queueing 100 mutations (paste,
230
+ * PPTX import, AI sandbox layer creation) reserializes the entire
231
+ * growing queue 100× — O(N²) `model.toJSON()`.
232
+ *
233
+ * @param mutation.capturedChanges - Pre-captured changes (frozen), used
234
+ * to avoid re-reading changes after pool ops that might clear them.
235
+ */
236
+ private queueMutation;
237
+ private syncScheduled;
238
+ private scheduleSync;
239
+ /**
240
+ * Persist mutation queue to IndexedDB
241
+ */
242
+ private persistMutationQueue;
243
+ /**
244
+ * Restore mutation queue from IndexedDB
245
+ */
246
+ private restoreMutationQueue;
247
+ /**
248
+ * Process pending mutations - can be called by SyncedStore when online
249
+ *
250
+ * Best Practice: Only sync models that still exist locally (local-first principle)
251
+ * - If a model was deleted locally → skip any pending updates/creates for it
252
+ * - This prevents "layer not found" errors from fast copy-paste-delete workflows
253
+ */
254
+ processPendingMutations(): Promise<void>;
255
+ /**
256
+ * Stage mutation to TransactionQueue - mutations in same tick are batched via microtask
257
+ *
258
+ * @param mutation.capturedChanges - Pre-captured changes to use instead of re-reading from model
259
+ */
260
+ private stageMutation;
261
+ /**
262
+ * Resolve conflicts between local and server data
263
+ * Used when processing deltas from WebSocket
264
+ *
265
+ * CRITICAL: Always respects certain server states (deletes, deactivations)
266
+ * even when there are local changes, to maintain data consistency.
267
+ */
268
+ resolveConflicts(localModel: Model, serverData: any): Model;
269
+ /**
270
+ * Extract critical state fields from server data
271
+ * These are states that must always be respected, even with local changes
272
+ */
273
+ private extractCriticalState;
274
+ /**
275
+ * Check if critical state changes exist that require forcing server state
276
+ */
277
+ private hasCriticalStateChange;
278
+ /**
279
+ * Handle network reconnection
280
+ */
281
+ private handleReconnection;
282
+ /**
283
+ * Handle network disconnection
284
+ */
285
+ private handleDisconnection;
286
+ /**
287
+ * Get current sync state
288
+ */
289
+ getState(): SyncState;
290
+ /**
291
+ * Set connection state
292
+ */
293
+ private setConnectionState;
294
+ /**
295
+ * Subscribe to events with disposer pattern
296
+ */
297
+ subscribe(event: string, handler: (data?: unknown) => void): () => void;
298
+ /**
299
+ * Add observer for sync events
300
+ */
301
+ addObserver(observer: SyncObserver): void;
302
+ /**
303
+ * Remove observer
304
+ */
305
+ removeObserver(observer: SyncObserver): void;
306
+ /**
307
+ * Notify all observers
308
+ */
309
+ private notifyObservers;
310
+ /**
311
+ * Disconnect from sync
312
+ */
313
+ disconnect(): void;
314
+ /**
315
+ * Mark the sync client as connected
316
+ * Called when WebSocket successfully connects (can happen independently of browser online/offline)
317
+ */
318
+ markConnected(): void;
319
+ /**
320
+ * Dispose and cleanup
321
+ */
322
+ dispose(): void;
323
+ /**
324
+ * LINEAR PATTERN: Notify TransactionQueue of incoming delta for sync ID threshold confirmation.
325
+ * Transactions are confirmed when any delta with id >= their lastSyncId threshold arrives.
326
+ * @param syncId - The sync ID of the received delta
327
+ */
328
+ onDeltaReceived(syncId: number): void;
329
+ /**
330
+ * LINEAR PATTERN: Cancel transactions for orphaned child entities
331
+ *
332
+ * Called by SyncedStore when a DELETE delta arrives for a parent entity.
333
+ * Cancels pending transactions for children that reference the deleted parent.
334
+ *
335
+ * @param childModelName - The child model type (e.g., 'SlideLayer')
336
+ * @param foreignKey - The FK property name (e.g., 'slideId')
337
+ * @param parentId - The deleted parent's ID
338
+ * @returns Number of transactions cancelled
339
+ */
340
+ cancelTransactionsByForeignKey(childModelName: string, foreignKey: string, parentId: string): number;
341
+ /**
342
+ * Wait for a transaction to be confirmed via delta echo (Linear pattern)
343
+ * Delegates to TransactionQueue which already handles timeouts
344
+ */
345
+ waitForDeltaConfirmation(transactionId: string): Promise<void>;
346
+ /**
347
+ * Force sync now - process pending mutations
348
+ */
349
+ syncNow(): Promise<void>;
350
+ /**
351
+ * Get sync statistics. Return type is inferred from the literal so
352
+ * the call site sees the actual shape — `connectionState` narrowed
353
+ * to its three states, `objectPoolStats` typed by `ObjectPool.getStats`.
354
+ */
355
+ getSyncStats(): {
356
+ connectionState: 'connected' | 'disconnected' | 'connecting';
357
+ pendingMutations: number;
358
+ objectPoolStats: ReturnType<ObjectPool['getStats']>;
359
+ };
360
+ /**
361
+ * Get pending transaction count from TransactionQueue
362
+ * Used by SyncedStore to compute hasUnsyncedChanges
363
+ */
364
+ getPendingTransactionCount(): number;
365
+ /**
366
+ * Subscribe to transaction events for sync status tracking
367
+ * Returns unsubscribe function
368
+ */
369
+ onTransactionEvent(event: 'created' | 'completed' | 'failed', callback: () => void): () => void;
370
+ /**
371
+ * Subscribe to mutation failures with the full payload. Mirrors the
372
+ * underlying TransactionQueue 'transaction:failed' shape so consumers
373
+ * can render typed UI (toast keyed by `AbloError.type`, route-level
374
+ * "this entity reverted" boundaries, telemetry).
375
+ *
376
+ * Distinct from `onTransactionEvent('failed', cb)`, which exists only
377
+ * for the legacy parameterless `pendingChanges` counter and intentionally
378
+ * drops the payload. The two coexist — keep the counter callback fast
379
+ * and the typed listener for user-visible surfaces.
380
+ */
381
+ onMutationFailure(listener: (payload: {
382
+ transaction: import('./transactions/TransactionQueue.js').Transaction;
383
+ error: Error;
384
+ permanent?: boolean;
385
+ }) => void): () => void;
386
+ /**
387
+ * Wait for the latest in-flight transaction for (modelName, modelId)
388
+ * to be confirmed by the server, or reject if it's rolled back.
389
+ * Resolves immediately when no transaction is in flight — see
390
+ * `TransactionQueue.confirmationFor` for the lookup contract.
391
+ *
392
+ * Distinct from `waitForDeltaConfirmation(transactionId)` which keys
393
+ * off a known tx id; this variant is for call sites that hold a
394
+ * Model reference but never see the underlying transaction.
395
+ */
396
+ waitForConfirmation(modelName: string, modelId: string): Promise<void>;
397
+ /**
398
+ * Get detailed debug info for the sync debug page
399
+ */
400
+ getDebugInfo(): {
401
+ connectionState: "connected" | "disconnected" | "connecting";
402
+ pendingMutationsCount: number;
403
+ transactionQueue: {
404
+ lastSeenSyncId: number;
405
+ awaitingDeltaCount: number;
406
+ awaitingDeltaTransactions: {
407
+ id: string;
408
+ type: "update" | "create" | "delete" | "archive" | "unarchive";
409
+ modelName: string;
410
+ modelId: string;
411
+ syncIdNeeded: number | undefined;
412
+ createdAt: number;
413
+ age: number;
414
+ }[];
415
+ pendingTransactions: {
416
+ id: string;
417
+ type: "update" | "create" | "delete" | "archive" | "unarchive";
418
+ modelName: string;
419
+ modelId: string;
420
+ }[];
421
+ executingTransactions: {
422
+ id: string;
423
+ type: "update" | "create" | "delete" | "archive" | "unarchive";
424
+ modelName: string;
425
+ modelId: string;
426
+ }[];
427
+ };
428
+ };
429
+ unassignEntity(entityType: string, entityId: string): Promise<void>;
430
+ reassignEntity(entityType: string, entityId: string, assigneeType: string, assigneeId: string, id?: string): Promise<void>;
431
+ /**
432
+ * Apply a batch of delta results from Database to the ObjectPool.
433
+ * Owns: model creation, upsert, remove, archive, conflict resolution.
434
+ * Returns: nothing — ObjectPool is updated in place.
435
+ */
436
+ /**
437
+ * Mark a local transaction as optimistically applied. The matching
438
+ * server delta (when it arrives with the same `transactionId`) will
439
+ * be recognized as an echo and skip the pool mutation. Called
440
+ * automatically by `TransactionQueue` when a transaction is staged;
441
+ * exposed publicly so tests can drive the API directly.
442
+ */
443
+ markTransactionPending(transactionId: string): void;
444
+ /**
445
+ * Read echo-detection counters: hits, rollbacks, evictions, and the
446
+ * current pending-set size. Surfaced for production observability
447
+ * — a sustained `evictions > 0` rate or `rollbacks` spike is a
448
+ * health signal worth alerting on.
449
+ */
450
+ getEchoMetrics(): Readonly<OptimisticEchoMetrics>;
451
+ /**
452
+ * Package-internal accessor for the TransactionQueue. Used by
453
+ * `Ablo.commits.create()` to route raw multi-op envelopes through the
454
+ * same retry-on-reconnect lane as the Model proxy path, and by tests
455
+ * to exercise the queue ↔ markTransactionPending wiring on the real
456
+ * instance the SyncClient subscribes to. NOT re-exported to SDK
457
+ * consumers — `Ablo` itself is the public surface.
458
+ */
459
+ getTransactionQueue(): TransactionQueue;
460
+ applyDeltaBatchToPool(dbResults: Array<{
461
+ action: string;
462
+ modelName: string;
463
+ modelId: string;
464
+ data?: Record<string, unknown> | null;
465
+ /**
466
+ * Server-stamped transaction id, echoing the client's commit op
467
+ * id. Used by echo detection to recognize "this is the
468
+ * confirmation of a mutation I've already applied locally."
469
+ * Optional because system-emitted deltas (sync_group changes,
470
+ * schema-derived deltas, etc.) don't have a client transaction.
471
+ */
472
+ transactionId?: string;
473
+ }>, enrichRelations: (modelName: string, data: Record<string, unknown>) => Record<string, unknown>): void;
474
+ /**
475
+ * Apply bootstrap data to the ObjectPool with ghost removal.
476
+ * Owns: model creation, batch upsert, ghost detection + removal.
477
+ */
478
+ applyBootstrapDataToPool(bootstrapData: {
479
+ models?: Record<string, unknown[]>;
480
+ failedModels?: string[];
481
+ }, protectedIds?: ReadonlySet<string>): {
482
+ added: number;
483
+ updated: number;
484
+ removed: number;
485
+ skipped: number;
486
+ healed: number;
487
+ };
488
+ }
489
+ export {};