@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,95 @@
1
+ /**
2
+ * Headless DI interfaces — the three abstractions createSyncEngine needs
3
+ * to run in Node.js without browser globals.
4
+ *
5
+ * STATUS: Type stubs only. No implementation yet. These interfaces are
6
+ * the deliverable of Path C Phase 1. When all three have both a browser
7
+ * implementation and a Node implementation, the SDK is truly headless.
8
+ *
9
+ * Usage (once implemented):
10
+ *
11
+ * // Browser (default — no DI needed, uses browser globals)
12
+ * const engine = createSyncEngine({ url, userId, organizationId });
13
+ *
14
+ * // Node.js / agent / sidecar (headless — DI overrides)
15
+ * import { inMemoryStorage, alwaysOnline } from '@ablo/sync-engine/headless';
16
+ * const engine = createSyncEngine({
17
+ * url, userId, organizationId,
18
+ * storage: inMemoryStorage(),
19
+ * network: alwaysOnline(),
20
+ * });
21
+ *
22
+ * The `transport` override is optional because Node 22 has a built-in
23
+ * global `WebSocket` class. It's included for testing (mock WebSocket)
24
+ * and for environments where the built-in WebSocket isn't available
25
+ * (Node 20, Deno, Bun — each has its own WebSocket story).
26
+ *
27
+ * See also: apps/sync-server/src/sdk-headless-entrypoint.test.ts
28
+ * (the skipped tests that become the acceptance tests for Phase 1)
29
+ */
30
+ /** A record in an object store. */
31
+ export type StorageRecord = Record<string, unknown> & {
32
+ id: string;
33
+ };
34
+ /** An object store — analogous to an IDBObjectStore. */
35
+ export interface ObjectStore {
36
+ get(id: string): Promise<StorageRecord | undefined>;
37
+ getAll(): Promise<StorageRecord[]>;
38
+ getAllFromIndex(indexName: string, value: string): Promise<StorageRecord[]>;
39
+ put(record: StorageRecord): Promise<void>;
40
+ delete(id: string): Promise<void>;
41
+ clear(): Promise<void>;
42
+ }
43
+ /** A database — analogous to an IDBDatabase. */
44
+ export interface StorageDatabase {
45
+ getStore(name: string): ObjectStore | undefined;
46
+ close(): void;
47
+ }
48
+ /**
49
+ * Opens or creates a named database.
50
+ *
51
+ * Browser: wraps indexedDB.open(name, version)
52
+ * Node: returns an in-memory database backed by Maps
53
+ */
54
+ export interface StorageProvider {
55
+ open(name: string, version?: number): Promise<StorageDatabase>;
56
+ delete(name: string): Promise<void>;
57
+ }
58
+ export interface NetworkProvider {
59
+ /** Current online status. */
60
+ isOnline(): boolean;
61
+ /**
62
+ * Subscribe to online/offline transitions. Returns an unsubscribe function.
63
+ * The callback receives `true` when coming online, `false` when going offline.
64
+ *
65
+ * Browser: listens for window 'online'/'offline' events + visibilitychange
66
+ * Node: never fires (always online)
67
+ * Test: fires when the test calls setOnline(true/false)
68
+ */
69
+ onStatusChange(callback: (online: boolean) => void): () => void;
70
+ }
71
+ /** Minimal WebSocket interface that SyncWebSocket uses. */
72
+ export interface WebSocketLike {
73
+ readonly readyState: number;
74
+ readonly bufferedAmount: number;
75
+ send(data: string | ArrayBuffer): void;
76
+ close(code?: number, reason?: string): void;
77
+ ping?(): void;
78
+ addEventListener(type: 'open', handler: () => void): void;
79
+ addEventListener(type: 'close', handler: (event: {
80
+ code: number;
81
+ reason: string;
82
+ }) => void): void;
83
+ addEventListener(type: 'message', handler: (event: {
84
+ data: unknown;
85
+ }) => void): void;
86
+ addEventListener(type: 'error', handler: (event: unknown) => void): void;
87
+ removeEventListener(type: string, handler: (...args: unknown[]) => void): void;
88
+ }
89
+ export interface TransportProvider {
90
+ /**
91
+ * Create a WebSocket connection to the given URL.
92
+ * Returns an object satisfying the WebSocketLike interface.
93
+ */
94
+ connect(url: string): WebSocketLike;
95
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Headless DI interfaces — the three abstractions createSyncEngine needs
3
+ * to run in Node.js without browser globals.
4
+ *
5
+ * STATUS: Type stubs only. No implementation yet. These interfaces are
6
+ * the deliverable of Path C Phase 1. When all three have both a browser
7
+ * implementation and a Node implementation, the SDK is truly headless.
8
+ *
9
+ * Usage (once implemented):
10
+ *
11
+ * // Browser (default — no DI needed, uses browser globals)
12
+ * const engine = createSyncEngine({ url, userId, organizationId });
13
+ *
14
+ * // Node.js / agent / sidecar (headless — DI overrides)
15
+ * import { inMemoryStorage, alwaysOnline } from '@ablo/sync-engine/headless';
16
+ * const engine = createSyncEngine({
17
+ * url, userId, organizationId,
18
+ * storage: inMemoryStorage(),
19
+ * network: alwaysOnline(),
20
+ * });
21
+ *
22
+ * The `transport` override is optional because Node 22 has a built-in
23
+ * global `WebSocket` class. It's included for testing (mock WebSocket)
24
+ * and for environments where the built-in WebSocket isn't available
25
+ * (Node 20, Deno, Bun — each has its own WebSocket story).
26
+ *
27
+ * See also: apps/sync-server/src/sdk-headless-entrypoint.test.ts
28
+ * (the skipped tests that become the acceptance tests for Phase 1)
29
+ */
30
+ export {};
31
+ // ── Factory functions (to be implemented in Phase 1) ─────────────────────
32
+ //
33
+ // These will be the public API that headless consumers import:
34
+ //
35
+ // import { inMemoryStorage, alwaysOnline } from '@ablo/sync-engine/headless';
36
+ //
37
+ // Stubs below show the intended signatures. Implementation is Phase 1 work.
38
+ // export function inMemoryStorage(): StorageProvider { ... }
39
+ // export function alwaysOnline(): NetworkProvider { ... }
40
+ // export function nodeWebSocket(): TransportProvider { ... }
41
+ // export function controllableNetwork(): NetworkProvider & { setOnline(v: boolean): void } { ... }
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Sync Engine SDK — Dependency Injection Interfaces
3
+ *
4
+ * These interfaces decouple the SDK from any specific app framework.
5
+ * Consumers implement them to wire in their own logging, observability,
6
+ * GraphQL client, session handling, and analytics.
7
+ */
8
+ export interface SyncLogger {
9
+ debug(message: string, ...args: unknown[]): void;
10
+ info(message: string, ...args: unknown[]): void;
11
+ warn(message: string, ...args: unknown[]): void;
12
+ error(message: string, ...args: unknown[]): void;
13
+ }
14
+ /** Breadcrumb severity levels */
15
+ export type BreadcrumbLevel = 'debug' | 'info' | 'warning' | 'error';
16
+ /** Breadcrumb categories for sync engine lifecycle events */
17
+ export type SyncBreadcrumbCategory = 'sync.bootstrap' | 'sync.transaction' | 'sync.websocket' | 'sync.offline' | 'sync.database' | 'sync.conflict' | 'sync.groups';
18
+ export interface RollbackDetails {
19
+ transactionType: string;
20
+ modelName: string;
21
+ modelId: string;
22
+ reason: string;
23
+ error?: string;
24
+ connectionState: string;
25
+ }
26
+ export interface TransactionFailureDetails {
27
+ context: string;
28
+ modelName?: string;
29
+ modelId?: string;
30
+ transactionId?: string;
31
+ error: Error | string;
32
+ }
33
+ export interface BootstrapFailureDetails {
34
+ attempt?: number;
35
+ type?: string;
36
+ navigatorOnline?: boolean;
37
+ }
38
+ export interface ReconciliationDetails {
39
+ reason: string;
40
+ model: string;
41
+ modelId: string;
42
+ syncIdNeeded?: number;
43
+ lastSeenSyncId: number;
44
+ retryCount: number;
45
+ connectionState?: string;
46
+ }
47
+ export interface DeltaRetryExhaustedDetails {
48
+ txId: string;
49
+ model: string;
50
+ modelId: string;
51
+ retryCount: number;
52
+ syncIdNeeded?: number;
53
+ }
54
+ export interface WebSocketErrorDetails {
55
+ context: string;
56
+ error?: string;
57
+ code?: number;
58
+ reason?: string;
59
+ }
60
+ export interface SelfHealingDetails {
61
+ modelName: string;
62
+ modelId: string;
63
+ field: string;
64
+ action: string;
65
+ }
66
+ export interface CommitZeroSyncIdDetails {
67
+ operationCount: number;
68
+ operations: string[];
69
+ }
70
+ export interface OfflineFlushFailureDetails {
71
+ error: string;
72
+ }
73
+ /** Span attributes for performance monitoring */
74
+ export interface SpanAttributes {
75
+ [key: string]: string | number | boolean | undefined;
76
+ }
77
+ /**
78
+ * Observability provider — replaces direct Sentry dependency.
79
+ * SDK ships a no-op default; consumers provide their own (e.g., Sentry, Datadog, OpenTelemetry).
80
+ */
81
+ export interface SyncObservabilityProvider {
82
+ /** Set user/org context for error grouping */
83
+ setContext(userId: string, organizationId: string): void;
84
+ /** Update connection state tag */
85
+ setConnectionState(state: 'connected' | 'disconnected' | 'connecting'): void;
86
+ /** Add a breadcrumb for sync lifecycle events */
87
+ breadcrumb(message: string, category: SyncBreadcrumbCategory, level?: BreadcrumbLevel, data?: Record<string, string | number | boolean | undefined>): void;
88
+ /** Capture optimistic rollback (data reverted) */
89
+ captureRollback(details: RollbackDetails): void;
90
+ /** Capture permanent transaction failure */
91
+ captureTransactionFailure(details: TransactionFailureDetails): void;
92
+ /** Capture bootstrap failure */
93
+ captureBootstrapFailure(error: Error | unknown, details?: BootstrapFailureDetails): void;
94
+ /** Capture reconciliation needed (delta confirmation timeout) */
95
+ captureReconciliation(details: ReconciliationDetails): void;
96
+ /** Capture delta retry exhausted */
97
+ captureDeltaRetryExhausted(details: DeltaRetryExhaustedDetails): void;
98
+ /** Capture WebSocket error */
99
+ captureWebSocketError(details: WebSocketErrorDetails): void;
100
+ /** Capture offline flush failure */
101
+ captureOfflineFlushFailure(details: OfflineFlushFailureDetails): void;
102
+ /** Capture self-healing event */
103
+ captureSelfHealing(details: SelfHealingDetails): void;
104
+ /** Capture commit returning lastSyncId: 0 */
105
+ captureCommitZeroSyncId(details: CommitZeroSyncIdDetails): void;
106
+ /** Wrap a synchronous function in a performance span */
107
+ startSpan<T>(name: string, op: string, fn: () => T, attributes?: SpanAttributes): T;
108
+ /** Wrap an async function in a performance span */
109
+ startSpanAsync<T>(name: string, op: string, fn: () => Promise<T>, attributes?: SpanAttributes): Promise<T>;
110
+ }
111
+ export interface SyncAnalytics {
112
+ capture(event: string, properties?: Record<string, unknown>): void;
113
+ }
114
+ /**
115
+ * Detects whether an error represents an expired/invalid session.
116
+ * The SDK uses this to decide whether to redirect to login vs retry.
117
+ */
118
+ export interface SessionErrorDetector {
119
+ /** Check if an error is a session error (401/403) */
120
+ isSessionError(error: unknown): boolean;
121
+ /** Check if an HTTP response status indicates a session error */
122
+ isSessionErrorResponse(status: number, body?: string): boolean;
123
+ }
124
+ export interface OnlineStatusProvider {
125
+ /** Returns true if the device is currently online */
126
+ isOnline(): boolean;
127
+ }
128
+ export interface ModelDebugLoggerContract {
129
+ logOperation(info: {
130
+ modelName: string;
131
+ modelId?: string;
132
+ operation: string;
133
+ fields?: Record<string, unknown>;
134
+ }): void;
135
+ logDebug(message: string): void;
136
+ logError(modelName: string, operation: string, message: string, data?: unknown): void;
137
+ logCreation(modelName: string, data: unknown, constructor: unknown): void;
138
+ logObservableSetup(modelName: string, observableProps: string[], computedProps: string[]): void;
139
+ }
140
+ /** Result of a successful `commit()` — server's sync cursor after the batch landed. */
141
+ export interface CommitResult {
142
+ lastSyncId: number;
143
+ }
144
+ /**
145
+ * Per-call knobs attached to any mutation. Mirrors Stripe's options
146
+ * object — the last argument of every `stripe.X.Y(...)` call. Optional
147
+ * everywhere; omitted fields fall back to sensible defaults.
148
+ *
149
+ * - `idempotencyKey` — when set, the server caches the response for 24h
150
+ * and returns the cached value on retries with the same key.
151
+ * When omitted, the SDK auto-generates a UUIDv4 per mutation so every
152
+ * call is retry-safe by default. Opt out with `{ idempotencyKey: null }`
153
+ * if you genuinely want retry-unsafe writes (rare).
154
+ * - `timeout` — abort the request if it takes longer than this many ms.
155
+ * No default (uses underlying transport's timeout).
156
+ * - `maxNetworkRetries` — retry with exponential backoff on 5xx / 429 /
157
+ * network errors. The same `idempotencyKey` is reused across retries
158
+ * so the server dedupes correctly. Default: 0.
159
+ * - `label` — human-readable audit tag. Flows to `mutation_log.label`
160
+ * server-side for operator debugging ("nightly cleanup", "user click").
161
+ */
162
+ export interface MutationOptions {
163
+ idempotencyKey?: string | null;
164
+ timeout?: number;
165
+ maxNetworkRetries?: number;
166
+ label?: string;
167
+ wait?: 'queued' | 'confirmed';
168
+ readAt?: number | null;
169
+ onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
170
+ intent?: string | {
171
+ readonly id: string;
172
+ } | null;
173
+ /**
174
+ * Active agent turn id to stamp on every delta row produced by this
175
+ * commit. Forwarded as the wire-level `causedByTaskId` field on the
176
+ * `{ type: 'commit' }` envelope. Set automatically by the SDK while
177
+ * `beginTurn(...)` is open.
178
+ */
179
+ causedByTaskId?: string | null;
180
+ }
181
+ /** A single mutation operation in a batch. `options` rides along so the
182
+ * server can cache+replay via `mutation_log`. */
183
+ export interface MutationOperation {
184
+ type: string;
185
+ model: string;
186
+ id: string;
187
+ input?: Record<string, unknown>;
188
+ /**
189
+ * Client-side transaction id for THIS operation. The server stamps
190
+ * it onto the resulting `sync_deltas.transaction_id` so the
191
+ * confirming delta can be recognized as an echo of the local
192
+ * optimistic mutation (echo detection at the receive layer drains
193
+ * the matching id via `OptimisticEchoTracker` and skips the pool
194
+ * mutation — see `SyncClient.applyDeltaBatchToPool`).
195
+ *
196
+ * Distinct from the batch-level `client_tx_id` used by
197
+ * `mutation_log` for idempotency. The mutation_log key dedupes a
198
+ * RETRIED batch (request-level cache); this transactionId
199
+ * identifies a specific MUTATION within a batch (per-row identity
200
+ * for echo matching). Both can coexist on the wire.
201
+ */
202
+ transactionId?: string;
203
+ readAt?: number | null;
204
+ onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
205
+ /**
206
+ * Per-op idempotency + audit metadata. `idempotencyKey` doubles as
207
+ * the `mutation_log.client_tx_id` cache key; `label` is persisted to
208
+ * `mutation_log.label` for debugging. Client-only fields (`timeout`,
209
+ * `maxNetworkRetries`) are handled at the transport layer and are
210
+ * NOT sent over the wire.
211
+ */
212
+ options?: Pick<MutationOptions, 'idempotencyKey' | 'label'>;
213
+ }
214
+ /**
215
+ * Executes mutations against the backend.
216
+ * The SDK calls this interface; consumers implement it with their
217
+ * specific GraphQL client, REST API, or other transport.
218
+ */
219
+ export interface MutationExecutor {
220
+ /**
221
+ * Commit a batch of mutations atomically, returning the sync ack.
222
+ * `options` apply to the whole batch (timeout, retries) — per-op
223
+ * idempotencyKey/label live on each `MutationOperation`.
224
+ *
225
+ * Name matches the wire frame (`{ type: 'commit' }`) and the
226
+ * universal mental model for atomic writes (DB transactions, git,
227
+ * Firestore). Replaces the older `batchAck` name from the retired
228
+ * GraphQL path.
229
+ */
230
+ commit(operations: MutationOperation[], options?: MutationOptions): Promise<CommitResult>;
231
+ /** Execute a create mutation for a specific model */
232
+ executeCreate(modelName: string, id: string, input: Record<string, unknown>, clientMutationId?: string, options?: MutationOptions): Promise<void>;
233
+ /** Execute an update mutation for a specific model */
234
+ executeUpdate(modelName: string, modelId: string, data: Record<string, unknown>, clientMutationId?: string, options?: MutationOptions): Promise<CommitResult | null>;
235
+ /** Execute a delete mutation for a specific model */
236
+ executeDelete(modelName: string, modelId: string, clientMutationId?: string, options?: MutationOptions): Promise<void>;
237
+ /** Execute an archive mutation for a specific model */
238
+ executeArchive(modelName: string, modelId: string, clientMutationId?: string, options?: MutationOptions): Promise<void>;
239
+ /** Execute an unarchive mutation for a specific model */
240
+ executeUnarchive(modelName: string, modelId: string, clientMutationId?: string, options?: MutationOptions): Promise<void>;
241
+ /** Upload an attachment (optional, not all consumers need this) */
242
+ uploadAttachment?(id: string, input: Record<string, unknown>): Promise<{
243
+ url: string;
244
+ }>;
245
+ /** Batch upload attachments (optional) */
246
+ batchUploadAttachments?(items: Array<{
247
+ id: string;
248
+ input: Record<string, unknown>;
249
+ }>): Promise<Array<{
250
+ id: string;
251
+ url: string;
252
+ }>>;
253
+ /** Delete a subscription entity */
254
+ deleteSubscription?(entityType: string, entityId: string, txId: string): Promise<void>;
255
+ /** Delete a favorite entity */
256
+ deleteFavorite?(modelId: string, txId: string): Promise<void>;
257
+ /** Register a callback for session expiry detection */
258
+ onSessionExpired?(callback: () => void): void;
259
+ }
260
+ /**
261
+ * Dispatches queued offline mutations on reconnect.
262
+ * Replaces the massive switch statement in OfflineFlush.ts.
263
+ */
264
+ export interface MutationDispatcher {
265
+ dispatch(operationName: string, variables: Record<string, unknown>): Promise<void>;
266
+ }
267
+ /**
268
+ * Application-specific configuration for the sync engine.
269
+ * Replaces the 6 hardcoded config maps that were previously
270
+ * embedded in TransactionQueue, Database, and Model.
271
+ */
272
+ export interface SyncEngineConfig {
273
+ /**
274
+ * FK-ordered create priority, keyed by the typename each model reports
275
+ * via {@link Model.getModelName}. `TransactionQueue` consults this at
276
+ * enqueue time and when sorting groups inside a batch — lower numbers
277
+ * execute first, so parents precede children.
278
+ *
279
+ * `createSyncEngine` populates this automatically by topologically
280
+ * walking `belongsTo` relations: a model with no FK parents gets 10, a
281
+ * child gets 20, a grandchild 30, and so on (step = 10 to leave room
282
+ * for consumer overrides). Apps rarely need to touch this — override
283
+ * through `configOverrides.modelCreatePriority` only when the schema's
284
+ * declared relations don't reflect an operational constraint (e.g. a
285
+ * polymorphic FK the SDK can't see).
286
+ */
287
+ modelCreatePriority: ReadonlyMap<string, number>;
288
+ /**
289
+ * Priority assigned to CREATE ops for models missing from
290
+ * {@link modelCreatePriority}. Falls between the typical top and bottom
291
+ * of the FK chain, so an unregistered model ends up later than declared
292
+ * parents but earlier than declared grandchildren — a safe middle.
293
+ */
294
+ defaultCreatePriority: number;
295
+ /**
296
+ * Priority for UPDATE/DELETE/ARCHIVE/UNARCHIVE ops, which don't need FK
297
+ * ordering (the row already exists by the time they run). Must be higher
298
+ * than any realistic CREATE priority so creates drain first.
299
+ */
300
+ defaultNonCreatePriority: number;
301
+ /**
302
+ * Essential fields preserved during partial UPDATE merges in IndexedDB.
303
+ * Prevents losing critical fields when a delta only contains changed fields.
304
+ * e.g., { Task: ['title', 'projectId'], Slide: ['deckId', 'order'] }
305
+ */
306
+ essentialFields: Readonly<Record<string, readonly string[]>>;
307
+ /**
308
+ * Fallback class name → model name mapping for Model.getModelName().
309
+ * Used when the ModelRegistry lookup fails (e.g., minified class names).
310
+ * e.g., { TaskModel: 'Task', ProjectModel: 'Project' }
311
+ */
312
+ classNameFallbackMap: Readonly<Record<string, string>>;
313
+ }
314
+ /**
315
+ * Allows consumers to extend the WebSocket event map with
316
+ * application-specific collaboration events (cursors, selections, etc.).
317
+ */
318
+ export interface WebSocketEventConfig {
319
+ /** Additional event type names beyond the core delta/presence/bootstrap events */
320
+ customEventTypes?: readonly string[];
321
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Sync Engine SDK — Dependency Injection Interfaces
3
+ *
4
+ * These interfaces decouple the SDK from any specific app framework.
5
+ * Consumers implement them to wire in their own logging, observability,
6
+ * GraphQL client, session handling, and analytics.
7
+ */
8
+ export {};
@@ -0,0 +1,36 @@
1
+ /**
2
+ * RecordingTransaction — wraps a base `Transaction` and captures inverse ops
3
+ * for the undo system. Each write is observed BEFORE it runs (to snapshot
4
+ * pre-state) and AFTER (to capture the forward op for redo).
5
+ *
6
+ * The wrapped mutator sees the exact same `Transaction<S>` shape; recording
7
+ * is invisible. When the mutator returns, the caller reads `getEntry()` and
8
+ * pushes it into the active `UndoScope`.
9
+ *
10
+ * Why snapshots live here (not in the UndoScope):
11
+ * - Update inverse requires `prev` field values — must be captured before
12
+ * the write lands in the pool.
13
+ * - Delete inverse requires the full model data — same reason.
14
+ * - Create inverse is simpler (delete by id) but the id must be known
15
+ * post-creation (schema generates UUIDs if caller omitted one).
16
+ */
17
+ import type { Schema } from '../schema/schema.js';
18
+ import type { SyncStoreContract } from '../react/context.js';
19
+ import type { Transaction } from './Transaction.js';
20
+ import type { UndoEntry } from './UndoManager.js';
21
+ export interface RecordingTransaction<S extends Schema> {
22
+ /** The wrapped transaction — pass this into the mutator. */
23
+ tx: Transaction<S>;
24
+ /**
25
+ * Finalize the recording. Returns the captured entry or `null` if the
26
+ * mutator made no reversible writes (skip the push to save memory).
27
+ */
28
+ getEntry: (label?: string) => UndoEntry | null;
29
+ }
30
+ /**
31
+ * Build a transaction that records inverses + forwards as it runs.
32
+ * Consumers use this only when they want the invocation to be undoable;
33
+ * read-only or side-effect-only mutators should use `createTransaction`
34
+ * directly to avoid the bookkeeping overhead.
35
+ */
36
+ export declare function createRecordingTransaction<S extends Schema>(schema: S, store: SyncStoreContract, organizationId: string): RecordingTransaction<S>;