@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,709 @@
1
+ /**
2
+ * BaseSyncedStore — Generic sync store base class for the SDK.
3
+ *
4
+ * Exports the core types, interfaces, and a base class that app-specific
5
+ * stores extend. The base class provides query/mutation/delta/bootstrap
6
+ * orchestration. Subclasses add domain-specific lazy-loading, collaboration
7
+ * events, and model enrichment.
8
+ *
9
+ * Design: The app's SyncedStore extends this and adds its own methods.
10
+ * This file only contains types and the abstract contract — the actual
11
+ * implementation stays in the app's SyncedStore.ts until we incrementally
12
+ * pull generic methods into this base class.
13
+ */
14
+ import { ConnectionManager } from './sync/ConnectionManager.js';
15
+ import type { SyncClient } from './SyncClient.js';
16
+ import type { Database, BootstrapResult } from './Database.js';
17
+ import type { ObjectPool } from './ObjectPool.js';
18
+ import { ModelRegistry } from './ModelRegistry.js';
19
+ import { SyncWebSocket, type SyncDelta, type SyncGroupChangePayload, type GroupAddedPayload, type GroupRemovedPayload, type VersionVector, type BootstrapHint, type BootstrapDataEvent, type PresenceUpdateEvent, type EventMap, type DefaultCollaborationEvents } from './sync/SyncWebSocket.js';
20
+ import { QueryProcessor } from './core/QueryProcessor.js';
21
+ import { Model } from './Model.js';
22
+ import { ModelScope } from './ObjectPool.js';
23
+ import type { Schema } from './schema/schema.js';
24
+ import { type ReaderActions } from './react/useReader.js';
25
+ /** Constructor type for Model subclasses (accepts abstract classes) */
26
+ export type ModelConstructor<T extends Model> = abstract new (...args: never[]) => T;
27
+ /** Concrete constructor type for instantiation */
28
+ export type ConcreteModelConstructor<T extends Model> = new (data?: any) => T;
29
+ /** Generic record type for model data */
30
+ export type ModelData = Record<string, unknown>;
31
+ /** Query result interface */
32
+ export interface QueryResult<T extends Model> {
33
+ data: T[];
34
+ total: number;
35
+ hasMore: boolean;
36
+ fromCache?: boolean;
37
+ }
38
+ /** A foreign-key index to register on the ObjectPool at construction time. */
39
+ export interface ForeignKeyIndexSpec {
40
+ /**
41
+ * The child model name (where the FK field lives) — this is the type
42
+ * that will be passed to `pool.registerForeignKey(modelName, fieldName)`
43
+ * and later to `pool.getByForeignKey(modelName, fieldName, value)`.
44
+ *
45
+ * Use the wire `__typename` casing (e.g., `'SlideLayer'`, not
46
+ * `'slideLayer'`) — that's the value `createFromData` stamps onto
47
+ * models and the pool indexes by.
48
+ */
49
+ readonly modelName: string;
50
+ /** The FK field name on the child model, e.g. `'slideId'`. */
51
+ readonly fieldName: string;
52
+ }
53
+ /**
54
+ * A declarative enrichment rule for the delta-apply path.
55
+ *
56
+ * When a delta for `modelName` arrives, after the model is constructed
57
+ * the base store reads `data[foreignKey]` from the payload, looks up
58
+ * the matching parent in the ObjectPool, and attaches it as
59
+ * `data[relationKey]`. Best-effort: if the parent isn't yet in the
60
+ * pool (e.g., arrived later in the same bootstrap batch), enrichment
61
+ * silently no-ops.
62
+ *
63
+ * Replaces the previous pattern of overriding `enrichRelations` on a
64
+ * subclass to hardcode per-model enrichment logic.
65
+ */
66
+ export interface EnrichmentPlanEntry {
67
+ /** The child model whose incoming deltas should be enriched. */
68
+ readonly modelName: string;
69
+ /** The FK field on the child that points at the parent's id. */
70
+ readonly foreignKey: string;
71
+ /** The property name under which to attach the parent model. */
72
+ readonly relationKey: string;
73
+ }
74
+ /** Configuration for SyncedStore behavior */
75
+ export interface SyncedStoreConfig {
76
+ enableOffline?: boolean;
77
+ enableCache?: boolean;
78
+ enableTelemetry?: boolean;
79
+ /**
80
+ * Initial version vector keys, each seeded to 0. Merged with the
81
+ * schema-derived set (if a schema is provided to the constructor) —
82
+ * explicit keys here layer on top of derived ones. Replaces the
83
+ * subclass pattern of hardcoding `this.versionVector = { tasks: 0, ... }`
84
+ * in the constructor.
85
+ */
86
+ versionVectorKeys?: readonly string[];
87
+ /**
88
+ * Declarative enrichment plan consumed by `enrichRelations`. Replaces
89
+ * the subclass override of `enrichRelations` for per-model parent
90
+ * attachment. Merged with schema-derived entries (relations marked
91
+ * `{ enrich: true }` on `belongsTo`).
92
+ */
93
+ enrichmentPlan?: readonly EnrichmentPlanEntry[];
94
+ /**
95
+ * Foreign-key indexes to register on the ObjectPool at construction
96
+ * time. Replaces the subclass override of `registerForeignKeys` for
97
+ * per-model FK registration. Merged with schema-derived entries
98
+ * (relations marked `{ index: true }` on `belongsTo`). Both sets
99
+ * are registered before the legacy `registerForeignKeys()` hook
100
+ * fires, so subclasses can still add more on top.
101
+ */
102
+ foreignKeyIndexes?: readonly ForeignKeyIndexSpec[];
103
+ }
104
+ /** Sync status for UI binding */
105
+ export interface SyncStatus {
106
+ state: 'idle' | 'syncing' | 'error' | 'offline' | 'reconnecting';
107
+ progress: number;
108
+ error?: Error;
109
+ /** When true, the error is a session/auth error requiring re-authentication. */
110
+ isSessionError: boolean;
111
+ lastSyncAt?: Date;
112
+ pendingChanges: number;
113
+ offlineSince?: Date;
114
+ }
115
+ /** User context for initialization */
116
+ export interface UserContext {
117
+ userId: string;
118
+ organizationId: string;
119
+ role?: string;
120
+ teamIds?: string[];
121
+ /** Participant kind on the wire. Default 'user' for browser
122
+ * sessions; 'agent' for headless bots / worker processes. The
123
+ * store routes this to SyncWebSocket so the WS URL carries
124
+ * `kind=agent` and the server applies capability-token auth. */
125
+ kind?: 'user' | 'agent' | 'system';
126
+ /** Biscuit capability token for `kind: 'agent'`. Sent as
127
+ * `?authorization=Bearer <token>` on the WS upgrade. */
128
+ capabilityToken?: string;
129
+ /** Server-authoritative sync groups, supplied by auth/capability
130
+ * exchange. The SDK does not invent org/user/default groups; app
131
+ * structure comes from schema-declared scopes and server-issued
132
+ * authorization. */
133
+ syncGroups?: readonly string[];
134
+ /**
135
+ * How aggressively this participant should pull baseline state at
136
+ * startup.
137
+ *
138
+ * - `'full'` (default): pull every delta in scope before `ready()`
139
+ * resolves. The standard browser/user replica behavior.
140
+ * - `'none'`: open the WebSocket and process live deltas only.
141
+ * Reads go through `resource.retrieve()` / filtered subscriptions
142
+ * backfilled by `Covering` deltas. Suitable for transactional
143
+ * participants — agent-worker, video-pipeline, routine runners —
144
+ * that don't need a local replica of the org's tenant plane.
145
+ */
146
+ bootstrapMode?: 'full' | 'none';
147
+ }
148
+ /** Smart sync options */
149
+ export interface SmartSyncOptions {
150
+ maxDeltasBeforeBootstrap?: number;
151
+ maxBootstrapSize?: number;
152
+ batchingDelay?: number;
153
+ maxBatchSize?: number;
154
+ }
155
+ /** Rehydration statistics from bootstrap */
156
+ export interface RehydrationStats {
157
+ added: number;
158
+ updated: number;
159
+ removed: number;
160
+ skipped: number;
161
+ healed: number;
162
+ elapsedMs: number;
163
+ }
164
+ /** Bootstrap timeout configuration */
165
+ export declare const BOOTSTRAP_CONFIG: {
166
+ readonly OVERALL_TIMEOUT_MS: 15000;
167
+ readonly MAX_RETRY_ATTEMPTS: 3;
168
+ readonly RETRY_DELAY_MS: 500;
169
+ };
170
+ export { ModelScope };
171
+ export type { SyncDelta, SyncGroupChangePayload, GroupAddedPayload, GroupRemovedPayload, VersionVector, BootstrapHint, BootstrapDataEvent, PresenceUpdateEvent, };
172
+ /**
173
+ * BaseSyncedStore — abstract base for app-specific sync stores.
174
+ *
175
+ * Provides the dependency structure, observable status, and protected
176
+ * accessors that subclasses use. The actual sync orchestration (initialize,
177
+ * delta processing, bootstrap, query, save, delete, etc.) lives in the
178
+ * app's concrete subclass for now — methods will be pulled up into this
179
+ * base class incrementally as they are genericized.
180
+ *
181
+ * Subclasses MUST call `super(dependencies, config)` and then set up
182
+ * their own MobX observables.
183
+ *
184
+ * Generic over `TCollaboration` — an app-defined event map for real-time
185
+ * collaboration events (cursors, selections, presence beyond the core set).
186
+ * Subclasses pass their own event map to get typed `subscribe()` calls on
187
+ * the underlying SyncWebSocket without casts:
188
+ *
189
+ * @example
190
+ * interface AbloEvents {
191
+ * 'sheet:selection': [SheetSelectionEvent];
192
+ * 'slide:cursor': [SlideCursorEvent];
193
+ * }
194
+ * class SyncedStore extends BaseSyncedStore<AbloEvents> {
195
+ * subscribeToSlideCursor(handler: (e: SlideCursorEvent) => void) {
196
+ * return this.syncWebSocket?.subscribe('slide:cursor', handler);
197
+ * }
198
+ * }
199
+ */
200
+ /**
201
+ * Walk a schema and derive the three sync-plan arrays consumed by
202
+ * `BaseSyncedStore`'s constructor: version-vector keys, FK indexes to
203
+ * register on the pool, and the enrichment plan.
204
+ *
205
+ * Version vector keys are derived from each model's `typename` (lowercased
206
+ * to match the server's event-type convention — `'Task'` → `'task'`,
207
+ * `'SlideLayer'` → `'slidelayer'`). A fallback to the schema key applies
208
+ * when `typename` is unset, though `defineSchema()` now always resolves
209
+ * it during assembly so the fallback is defensive-only.
210
+ *
211
+ * FK indexes and enrichment entries are pulled from each `belongsTo`
212
+ * relation where `options.index` / `options.enrich` is set. Relations
213
+ * without those options are skipped — this is an opt-in mechanism so
214
+ * adding a `belongsTo` never silently changes delta or lookup semantics.
215
+ *
216
+ * Pure function: takes a Schema, returns three arrays. No side effects,
217
+ * no class state. Called once at construction time from `BaseSyncedStore`.
218
+ */
219
+ export declare function deriveSyncPlanFromSchema(schema: Schema): {
220
+ versionVectorKeys: string[];
221
+ enrichmentPlan: EnrichmentPlanEntry[];
222
+ foreignKeyIndexes: ForeignKeyIndexSpec[];
223
+ };
224
+ /**
225
+ * Schema-derived accessor namespace exposed on `store.query`. Each key is
226
+ * a model name from the schema and resolves to a `ReaderActions<S, K>`
227
+ * with `findById` / `findMany` / `findFirst` / `count`. Return types are
228
+ * inferred from the schema (`InferModel<S, K>`) so callers don't need to
229
+ * cast or pass class constructors.
230
+ *
231
+ * Prisma / Drizzle / Zero all use this shape: `store.query.<modelKey>.*`.
232
+ */
233
+ export type QueryNamespace<S extends Schema> = {
234
+ readonly [K in keyof S['models'] & string]: ReaderActions<S, K>;
235
+ };
236
+ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaboration> = DefaultCollaborationEvents, TSchema extends Schema = Schema> {
237
+ syncStatus: SyncStatus;
238
+ protected readonly syncClient: SyncClient;
239
+ protected readonly database: Database;
240
+ protected readonly objectPool: ObjectPool;
241
+ protected readonly modelRegistry: ModelRegistry;
242
+ /**
243
+ * Schema the store was constructed with. Persisted so the `query`
244
+ * accessor namespace can build typed per-model reader actions lazily
245
+ * without callers having to pass the schema at every lookup site.
246
+ */
247
+ protected readonly schema?: TSchema;
248
+ /** Lazily-built `query.<modelKey>.*` accessor namespace. */
249
+ private _queryProxy?;
250
+ protected syncWebSocket: SyncWebSocket<TCollaboration> | null;
251
+ private _syncServerUrl?;
252
+ /**
253
+ * Public accessor for the underlying SyncWebSocket. Used by the
254
+ * factory in `createSyncEngine` to wire the default mutation
255
+ * executor — the executor needs the WS handle to send commit
256
+ * frames, and the factory can't reach `protected` state through
257
+ * normal typing. Returns null until WS is initialized during
258
+ * `initialize()`.
259
+ */
260
+ getSyncWebSocket(): SyncWebSocket<TCollaboration> | null;
261
+ protected readonly queryProcessor: QueryProcessor;
262
+ /**
263
+ * Runtime behavior flags only — the three schema/config arrays
264
+ * (`versionVectorKeys`, `enrichmentPlan`, `foreignKeyIndexes`) are
265
+ * consumed at construction time and stored on the instance as
266
+ * `versionVector`, `enrichmentPlan`, and pool-registered indexes.
267
+ * They don't need to persist on `this.config`.
268
+ */
269
+ protected readonly config: Required<Pick<SyncedStoreConfig, 'enableOffline' | 'enableCache' | 'enableTelemetry'>>;
270
+ protected disposers: Array<() => void>;
271
+ protected initialized: boolean;
272
+ protected dataReady: boolean;
273
+ protected userContext: UserContext | null;
274
+ protected versionVector: VersionVector;
275
+ /**
276
+ * Declarative enrichment plan: "for model X, when a delta arrives,
277
+ * read data[foreignKey] and attach the matching parent from the pool
278
+ * as data[relationKey]." Merged from schema-derived + config at
279
+ * construction time. Replaces the `enrichRelations` subclass override
280
+ * pattern.
281
+ */
282
+ protected enrichmentPlan: readonly EnrichmentPlanEntry[];
283
+ protected smartSyncOptions: Required<SmartSyncOptions>;
284
+ protected pendingDeltas: SyncDelta[];
285
+ protected batchTimer: ReturnType<typeof setTimeout> | null;
286
+ protected syncPromise: Promise<void> | null;
287
+ protected lastAckedId: number;
288
+ protected highestProcessedSyncId: number;
289
+ protected bootstrapDeltaQueue: SyncDelta[] | null;
290
+ protected activeBootstrapCount: number;
291
+ protected pendingDeletes: Set<string>;
292
+ protected modelTypesHydrated: Set<string>;
293
+ protected modelTypeHydrationInFlight: Map<string, Promise<void>>;
294
+ constructor(dependencies: {
295
+ syncClient: SyncClient;
296
+ database: Database;
297
+ objectPool: ObjectPool;
298
+ modelRegistry: ModelRegistry;
299
+ /**
300
+ * Optional schema. When provided, `deriveSyncPlanFromSchema` walks
301
+ * the schema's models + relations to auto-populate version vector
302
+ * keys, FK indexes, and the enrichment plan from declarative
303
+ * annotations. Class-based subclass users (like Ablo's legacy
304
+ * SyncedStore) typically pass explicit `config.versionVectorKeys`
305
+ * / `config.foreignKeyIndexes` / `config.enrichmentPlan` instead.
306
+ */
307
+ schema?: TSchema;
308
+ /** Sync server URL for WebSocket connection. Converted to wss:// automatically. */
309
+ url?: string;
310
+ }, config?: SyncedStoreConfig);
311
+ /**
312
+ * Register foreign key indexes for O(1) lookups.
313
+ *
314
+ * Legacy override hook — in Phase 2 the preferred way to declare FK
315
+ * indexes is via `config.foreignKeyIndexes` at construction time, or
316
+ * by marking the `belongsTo` relation with `{ index: true }` in the
317
+ * schema. This hook still fires AFTER the schema-derived + config
318
+ * registrations, so subclasses can layer additional FKs on top.
319
+ */
320
+ protected registerForeignKeys(): void;
321
+ /**
322
+ * Enrich delta data with related models from the ObjectPool.
323
+ *
324
+ * Base implementation walks `this.enrichmentPlan` — entries populated
325
+ * from the schema's `{ enrich: true }` relations and from
326
+ * `config.enrichmentPlan`. Subclasses can still override for bespoke
327
+ * logic, calling `super.enrichRelations(modelName, data)` first to
328
+ * apply the declarative plan before layering on custom work.
329
+ *
330
+ * Enrichment is best-effort: if the parent isn't yet in the pool
331
+ * (e.g., a child delta arrives before its parent in a bootstrap
332
+ * batch), the entry is silently skipped and the data passes through
333
+ * untouched. The next delta for the same child will re-enrich.
334
+ */
335
+ protected enrichRelations(modelName: string, data: ModelData): ModelData;
336
+ /** Check if a model name represents a custom/dynamic entity type. */
337
+ protected isCustomEntity(modelName: string): boolean;
338
+ /** Create a custom entity instance from delta data. Override for domain-specific custom entities. */
339
+ protected createCustomEntity(_modelName: string, _modelId: string, _data: Record<string, unknown>): Model | null;
340
+ /** Called before save for domain-specific validation/self-healing. */
341
+ protected beforeSave(_model: Model): void;
342
+ /** Connection lifecycle event callback — set by subclass to wire connection state machine. */
343
+ protected onConnectionEvent?: (event: string) => void;
344
+ /**
345
+ * Internal connection FSM. Owns network probe + backoff + reconnect
346
+ * orchestration for the default path. Constructed lazily once we
347
+ * have a user context + a WebSocket (see `wireWebSocketEvents`);
348
+ * driven by the `onConnectionEvent` hook AND browser online/offline
349
+ * events it sets up itself.
350
+ *
351
+ * Every consumer gets production-grade offline-to-online recovery
352
+ * out of the box. Subclasses that want their own lifecycle owner
353
+ * can disable this by overriding `createConnectionManager()` to
354
+ * return null.
355
+ */
356
+ protected connectionManager: import('./sync/ConnectionManager.js').ConnectionManager | null;
357
+ /**
358
+ * Listeners registered via `subscribeSessionError()`. Fired when the
359
+ * WebSocket closes with a session-invalid code (1008/4001/4003) or a
360
+ * session-error event is received. Separate from `onConnectionEvent`
361
+ * (which exists for the ConnectionStore FSM) so multiple consumers —
362
+ * typically `<AbloProvider>` and a connection-lifecycle owner — can
363
+ * both react without racing on the single-callback slot.
364
+ */
365
+ protected sessionErrorListeners: Set<(error: Error) => void>;
366
+ /**
367
+ * Subscribe to session-error events. The returned function removes
368
+ * the listener. Safe to call multiple times from different consumers
369
+ * (each gets its own slot in the listener set).
370
+ */
371
+ subscribeSessionError(listener: (error: Error) => void): () => void;
372
+ /**
373
+ * Subscribe to per-mutation failure payloads. Forwarded from the
374
+ * underlying `SyncClient.transactionQueue` so consumers (toast layer,
375
+ * route-level reverted boundaries, telemetry) can react without
376
+ * reaching across the store. Returns an unsubscribe function.
377
+ *
378
+ * Why this lives on the base store rather than SyncClient: the React
379
+ * `<AbloProvider>` binds against this surface, so adding it here
380
+ * keeps the engine's internal wiring private while still giving the
381
+ * SDK a single hook to expose. Mirrors `subscribeSessionError` —
382
+ * same shape, same lifecycle.
383
+ */
384
+ subscribeMutationFailure(listener: (payload: {
385
+ transaction: import('./transactions/TransactionQueue.js').Transaction;
386
+ error: Error;
387
+ permanent?: boolean;
388
+ }) => void): () => void;
389
+ /**
390
+ * Wait for the in-flight transaction for (modelName, modelId) to be
391
+ * confirmed by the server. See `SyncClient.waitForConfirmation` for the
392
+ * lookup contract; resolves immediately if nothing is in flight.
393
+ */
394
+ waitForConfirmation(modelName: string, modelId: string): Promise<void>;
395
+ /**
396
+ * Execute a bootstrap function with timeout protection and automatic retry.
397
+ * Prevents the common issue where bootstrap hangs on startup.
398
+ */
399
+ protected executeBootstrapWithTimeout<T>(bootstrapFn: () => Promise<T>, _context: UserContext, signal?: AbortSignal): Promise<T>;
400
+ /** Create a timeout promise for bootstrap attempts */
401
+ protected createBootstrapTimeout(attempt: number): Promise<never>;
402
+ /** Reset bootstrap-related state for a clean retry */
403
+ protected resetBootstrapState(): void;
404
+ /** Perform reconnect: bootstrap + WS reconnect. Returns outcome for state machine. */
405
+ performReconnect(): Promise<'success' | 'session_error' | 'network_error'>;
406
+ /**
407
+ * Handle an actionType 'G' delta.
408
+ *
409
+ * The server emits 'G' via two distinct pathways, distinguished by payload
410
+ * shape:
411
+ *
412
+ * Incremental (EmitGroupAdded): { group, userId }
413
+ * - The recipient was added to a single sync group.
414
+ * - Subsequent 'C' (Covering) deltas deliver each newly-visible entity.
415
+ * - No re-bootstrap — entities arrive via the normal insert path.
416
+ *
417
+ * Legacy (EmitGroupChange): { addedGroups, removedGroups }
418
+ * - Single delta carrying the full group membership diff.
419
+ * - Forces a full re-bootstrap (disconnect + reconnect + fetch all).
420
+ * - Deprecated on the server; kept here for wire-level backward compat.
421
+ */
422
+ protected handleSyncGroupChange(delta: SyncDelta): Promise<void>;
423
+ /**
424
+ * Handle an incremental GroupAdded delta.
425
+ *
426
+ * Adds the new group to the subscription metadata without triggering a
427
+ * re-bootstrap. The server will follow up with 'C' (Covering) deltas for
428
+ * each newly-visible entity, which flow through the normal insert path.
429
+ */
430
+ protected handleGroupAdded(payload: GroupAddedPayload, syncId: number): Promise<void>;
431
+ /**
432
+ * Handle an actionType 'S' (GroupRemoved) delta.
433
+ *
434
+ * Signals that the recipient has lost access to a sync group. Because
435
+ * the client does not track per-entity group membership, we can't
436
+ * selectively purge entities belonging to that group. The safe fallback
437
+ * is the legacy behavior: clear local state and force a re-bootstrap
438
+ * with the updated group list.
439
+ *
440
+ * Future optimization: track group membership in the ObjectPool so 'S'
441
+ * can do a targeted purge instead of a full re-bootstrap.
442
+ */
443
+ protected handleGroupRemoved(delta: SyncDelta): Promise<void>;
444
+ /** Compute new sync groups after applying additions and removals */
445
+ protected computeUpdatedSyncGroups(payload: SyncGroupChangePayload): string[];
446
+ /** Force a full re-bootstrap via connection lifecycle event.
447
+ *
448
+ * No-op for `bootstrapMode: 'none'` participants — they never pull
449
+ * baseline state, so a "force re-bootstrap" trigger (sync-group
450
+ * shrink, scope revocation) instead just flushes the local pool and
451
+ * relies on covering deltas to repopulate the data they actually
452
+ * subscribe to.
453
+ */
454
+ protected forceFullRebootstrap(): void;
455
+ /**
456
+ * Single source of truth for the sync-group list this session is
457
+ * subscribed to. Server-issued (`context.syncGroups`) is authoritative.
458
+ * When absent, the SDK subscribes to no explicit groups. Both
459
+ * `checkSyncGroupShrinkage` and `setupWebSocketSync` resolve through
460
+ * here so the WS subscription and the security-critical shrinkage
461
+ * check can never disagree.
462
+ */
463
+ protected resolveSyncGroups(context: UserContext): readonly string[];
464
+ /** Check if sync groups shrank since last session — force full bootstrap if so */
465
+ protected checkSyncGroupShrinkage(): Promise<void>;
466
+ /** Apply bootstrap data to the ObjectPool with ghost removal */
467
+ /** Apply bootstrap data to the ObjectPool. Delegates pool writes to SyncClient. */
468
+ protected applyBootstrapToPool(bootstrapResult: BootstrapResult, protectedIds?: ReadonlySet<string>): RehydrationStats;
469
+ /**
470
+ * Initialize the sync engine with user context.
471
+ * Offline-first: hydrate from IDB → show UI → bootstrap from server in background.
472
+ */
473
+ initialize(context: UserContext, signal?: AbortSignal): Generator<Promise<unknown>, {
474
+ success: boolean;
475
+ error?: Error;
476
+ }, unknown>;
477
+ /** Background bootstrap — non-blocking, user sees cached data while this runs */
478
+ protected performBackgroundBootstrap(requirements: Awaited<ReturnType<typeof this.database.requiredBootstrap>>, context: UserContext, signal?: AbortSignal): Promise<void>;
479
+ /** Run bootstrap with delta queuing to prevent race conditions */
480
+ protected withDeltaQueuing<T>(fn: () => Promise<T>): Promise<T>;
481
+ /** Collect IDs that must survive ghost removal (added by deltas during bootstrap) */
482
+ protected collectDeltaProtectedIds(preBootstrapIds: ReadonlySet<string>): Set<string>;
483
+ /** Replay deltas queued during bootstrap */
484
+ protected replayQueuedDeltas(): void;
485
+ /**
486
+ * Factory for the internal `ConnectionManager`. Override to return
487
+ * `null` in subclasses that own their own connection lifecycle
488
+ * (tests, headless runners, custom FSM wrappers). Default builds a
489
+ * manager scoped to `_syncServerUrl` with production backoff.
490
+ *
491
+ * **Agent participants get `null`.** The FSM is wired around browser
492
+ * events (`visibilitychange`, `online`/`offline`, watchdog) which are
493
+ * meaningful for human-facing tabs and meaningless for headless agent
494
+ * processes. On agent hosts the FSM has no event source to drive
495
+ * recovery — and worse, its `offline` entry action calls
496
+ * `syncWebSocket.disconnect()` which sets `isManualClose=true` and
497
+ * cancels the reconnect that `SyncWebSocket.onclose` had just
498
+ * scheduled. The two recovery systems fight and the browser-only one
499
+ * wins by destroying the Node-compatible one's work. Returning `null`
500
+ * for agents leaves `SyncWebSocket`'s exponential-backoff
501
+ * `scheduleReconnect()` as the sole recovery path — which is correct
502
+ * for server-side agents whether they run on Node, Bun, Deno, or
503
+ * inside a Docker container with no `window`.
504
+ *
505
+ * Why gate on `kind` and not `typeof window`: env detection by global
506
+ * existence is fragile (SSR polyfills, jsdom, sandboxed hosts). The
507
+ * participant kind is the actual semantic axis — "is this a human-
508
+ * driven session" vs "is this a server agent". The latter never has
509
+ * a tab to lose focus or a network adapter to wake up.
510
+ */
511
+ protected createConnectionManager(kind?: 'user' | 'agent' | 'system'): ConnectionManager | null;
512
+ /** Disconnect and clean up all resources */
513
+ disconnect(): Promise<void>;
514
+ /**
515
+ * Destroy every IndexedDB database owned by the sync engine.
516
+ *
517
+ * First disconnects (releases WebSocket + timers + in-memory caches),
518
+ * then walks `indexedDB.databases()` and deletes any database whose
519
+ * name starts with `ablo_` or `ablo-`. This covers:
520
+ * - `ablo_<hash>` workspace data DBs
521
+ * - `ablo_databases` meta registry
522
+ * - `ablo-sync` offline mutation queue
523
+ *
524
+ * Use case: session expiry (previous-user data must not persist on
525
+ * disk before the next sign-in races into a corrupted state) or
526
+ * explicit user-initiated logout.
527
+ *
528
+ * Best-effort: swallows individual delete errors. Some browsers do
529
+ * not support `indexedDB.databases()` — the method returns without
530
+ * deleting in that case, same behavior as the pre-SDK app code.
531
+ */
532
+ purge(): Promise<void>;
533
+ /**
534
+ * Create WebSocket connection and wire all event handlers.
535
+ * Handles: deltas, batches, presence, bootstrap_required, errors, reconnection.
536
+ */
537
+ /**
538
+ * Block until the WebSocket reports a `connected` event, or until
539
+ * `timeoutMs` elapses (returns false on timeout, true on connect).
540
+ * Used by `initialize()` for `bootstrapMode: 'none'` consumers to
541
+ * honor `ready()`'s "WS is connected when this resolves" contract
542
+ * — `setupWebSocketSync` is fire-and-forget on the upgrade, and
543
+ * without an explicit wait the next mutation can race the open.
544
+ *
545
+ * Resolves immediately if the WS is already connected (e.g., warm
546
+ * reconnect after redeploy). Resolves false on timeout rather than
547
+ * throwing so initialize() can complete and let the caller's first
548
+ * mutation attempt surface a clearer error.
549
+ */
550
+ protected waitForWebSocketConnected(timeoutMs: number): Promise<boolean>;
551
+ protected setupWebSocketSync(context: UserContext, lastSyncId: number): void;
552
+ /** State signature for delta deduplication */
553
+ private extractStateSignature;
554
+ /** Get fields that represent meaningful state for deduplication. Override for model-specific fields. */
555
+ protected getStateFields(_modelName: string): string[];
556
+ private isSameState;
557
+ /** Deduplicate deltas to the same entity — keep meaningful state transitions only */
558
+ protected deduplicateDeltas(deltas: SyncDelta[]): SyncDelta[];
559
+ /** Process incoming delta with smart batching */
560
+ protected processDeltaWithBatching(delta: SyncDelta): void;
561
+ /**
562
+ * Cancel pending transactions for child entities when a parent is deleted.
563
+ *
564
+ * Uses `pool.getByForeignKey` (O(1) via the FK index registered at
565
+ * schema build time) to find children. The previous implementation did
566
+ * `getByType(ctor).filter(e => e.toJSON()[foreignKey] === parentId)` —
567
+ * a full pool scan per child model + a `toJSON()` allocation per
568
+ * candidate. For a deck delete with 10K layers in the pool, that was
569
+ * 10K toJSON allocations per cascade level. The FK-indexed lookup
570
+ * skips both the scan AND the allocation.
571
+ */
572
+ protected cascadeCancelTransactionsForDeletedParent(parentModelName: string, parentId: string): void;
573
+ /** Flush pending deltas with deduplication and batched ObjectPool mutations */
574
+ /** Flush pending deltas with deduplication. Delegates pool writes to SyncClient. */
575
+ protected flushPendingDeltas(): Promise<void>;
576
+ /** Check if a model type is local-only (no sync). Override for domain-specific models. */
577
+ protected isLocalOnlyModel(_modelName: string): boolean;
578
+ /** Validate model against schema before save */
579
+ protected validateModel(model: Model): void;
580
+ /**
581
+ * Save a model (create or update).
582
+ *
583
+ * Accepts any entity shape with `{ id: string }` so consumers can pass the
584
+ * Zod-inferred model types from `InferModel<Schema, K>` without knowing
585
+ * about the internal `Model` base class. At runtime, every entity reaching
586
+ * this method came through the object pool (via `store.create`, a query
587
+ * accessor, or an optimistic insert) and IS a `Model` instance — the one
588
+ * cast below preserves that invariant inside the SDK.
589
+ */
590
+ save<T extends {
591
+ id: string;
592
+ createdAt?: Date;
593
+ updatedAt?: Date;
594
+ }>(entity: T, options?: {
595
+ skipValidation?: boolean;
596
+ }): Promise<void>;
597
+ /** Save with an atomic server mutation (e.g., createSlideWithLayers) */
598
+ saveWithAtomicMutation(model: Model, mutation: (gql: unknown) => Promise<unknown>): Promise<void>;
599
+ /** Delete a model. Accepts schema-inferred entity shapes (see `save`). */
600
+ delete<T extends {
601
+ id: string;
602
+ }>(entity: T): Promise<void>;
603
+ /** Archive a model. Accepts schema-inferred entity shapes (see `save`). */
604
+ archive<T extends {
605
+ id: string;
606
+ archivedAt?: Date | null;
607
+ }>(entity: T): Promise<void>;
608
+ /** Unarchive a model. Accepts schema-inferred entity shapes (see `save`). */
609
+ unarchive<T extends {
610
+ id: string;
611
+ archivedAt?: Date | null;
612
+ }>(entity: T): Promise<void>;
613
+ /**
614
+ * Schema-keyed accessor namespace — the primary type-safe lookup surface.
615
+ *
616
+ * ```ts
617
+ * const chat = store.query.chats.retrieve(chatId); // Chat | undefined
618
+ * const slides = store.query.slides.findMany({ where: { deckId } }); // Slide[]
619
+ * ```
620
+ *
621
+ * Each `query.<modelKey>` is a `ReaderActions<Schema, K>` built lazily on
622
+ * first access via `createReaderActions`. The returned types are inferred
623
+ * from the schema (`InferModel<S, K>`), including `InferRelations` — so
624
+ * `chat.messages`, `slide.layers`, etc. are typed without a cast.
625
+ *
626
+ * Throws if the store was constructed without a schema (class-based
627
+ * subclasses that wire models via `modelRegistry.registerModel` directly
628
+ * don't have access to schema-derived inference).
629
+ */
630
+ get query(): QueryNamespace<TSchema>;
631
+ /** Retrieve a single entity by id. Synchronous pool read. */
632
+ retrieve(_modelClass: ModelConstructor<Model>, id: string): Model | undefined;
633
+ /** Find any entity by ID regardless of type */
634
+ findAnyById(id: string): Model | undefined;
635
+ /**
636
+ * Lookup a model by ID alone. Matches the `SyncStoreRef.getById` contract
637
+ * that schema-defined computeds use when they need to resolve a related
638
+ * entity without holding onto its constructor.
639
+ */
640
+ getById(id: string): Model | undefined;
641
+ /**
642
+ * Create a model instance locally, typed via the schema.
643
+ *
644
+ * ```ts
645
+ * const sheet = store.create('spreadsheetSheets', { name, spreadsheetId });
646
+ * // sheet: SpreadsheetSheet | null — no cast needed
647
+ * ```
648
+ *
649
+ * The `typename` arg is the schema key (camelCase plural, e.g.
650
+ * `'spreadsheetSheets'`); the returned instance has the
651
+ * `InferModel<Schema, K>` shape including computeds + relation accessors.
652
+ * Wraps `pool.create(...)` — the underlying runtime is unchanged, just
653
+ * type-narrowed.
654
+ */
655
+ create<K extends keyof TSchema['models'] & string>(typename: K, data: Record<string, unknown>): import('./schema/schema.js').InferModel<TSchema, K> | null;
656
+ /**
657
+ * Legacy class-based query entry point — kept for callers that still pass
658
+ * a Model constructor + options object. New code should use the typed
659
+ * `store.query.<modelKey>` namespace instead, which returns properly
660
+ * inferred schema types without needing a class value or cast.
661
+ */
662
+ queryByClass(modelClass: ModelConstructor<Model>, options?: {
663
+ predicate?: (model: Model) => boolean;
664
+ scope?: ModelScope;
665
+ orderBy?: keyof Model;
666
+ order?: 'asc' | 'desc';
667
+ limit?: number;
668
+ offset?: number;
669
+ }): QueryResult<Model>;
670
+ /**
671
+ * Get all models of a type. Returns Model[] honestly — callers that need
672
+ * narrow types should use `useAblo((ablo) => ablo.<model>.list(...))`
673
+ * which does proper inference via `InferModel<S, K>`.
674
+ */
675
+ allModelsOfType(modelClass: ModelConstructor<Model>, scope?: ModelScope): Model[];
676
+ /** Error handler for fire-and-forget flushPendingDeltas calls */
677
+ protected handleFlushError: (error: unknown) => void;
678
+ /** Process a single delta (used for immediate DELETE processing). Override for domain-specific handling. */
679
+ protected processDelta(delta: SyncDelta): Promise<void>;
680
+ /** Handle bootstrap_required event */
681
+ protected handleBootstrapRequired(_hint: BootstrapHint): void;
682
+ /** Handle bootstrap_data event. Override in subclass. */
683
+ protected handleBootstrapData(_data: BootstrapDataEvent): void;
684
+ /** Handle presence_update event. Override in subclass. */
685
+ protected handlePresenceUpdate(_data: PresenceUpdateEvent): void;
686
+ protected incrementPendingChanges(): void;
687
+ protected decrementPendingChanges(): void;
688
+ protected updateSyncStatus(updates: Partial<SyncStatus>): void;
689
+ get pool(): ObjectPool;
690
+ get lastSyncId(): number;
691
+ get isReady(): boolean;
692
+ get isSyncing(): boolean;
693
+ get isOffline(): boolean;
694
+ get isReconnecting(): boolean;
695
+ get isError(): boolean;
696
+ get hasUnsyncedChanges(): boolean;
697
+ /** The SyncWebSocket handle — for collaboration events. */
698
+ get ws(): SyncWebSocket<TCollaboration> | null;
699
+ /** The Database instance — for demand loaders and direct IDB operations. */
700
+ get db(): Database;
701
+ /** The SyncClient instance — for assignment operations and other direct sync actions. */
702
+ get sc(): SyncClient;
703
+ /** The current organization ID — from the last initialize() call. */
704
+ get orgId(): string | undefined;
705
+ /** Count models matching a predicate. */
706
+ count(modelClass: ModelConstructor<Model>, predicate?: (m: Model) => boolean): number;
707
+ /** Get entities by foreign key (used by Model subclasses via Model.store) */
708
+ getByForeignKey(modelName: string, foreignKey: string, id: string): Model[];
709
+ }