@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,64 @@
1
+ /**
2
+ * QueryView<T> — Incrementally maintained materialized view for a single query.
3
+ *
4
+ * Instead of scanning the full collection on every render, a QueryView
5
+ * builds the result set once, then maintains it via handleAdded /
6
+ * handleUpdated / handleRemoved calls from the ViewRegistry.
7
+ *
8
+ * `results` is a stable MobX observable array — components observe it
9
+ * directly and receive granular updates (splice/push, never replacement).
10
+ */
11
+ import { type IObservableArray } from 'mobx';
12
+ import { ModelScope } from '../types/index.js';
13
+ import type { ObjectPool } from '../ObjectPool.js';
14
+ import type { ViewRegistry } from './ViewRegistry.js';
15
+ import type { IncrementalView } from './query-utils.js';
16
+ export interface QueryViewOptions<T> {
17
+ where?: Partial<T>;
18
+ filter?: (entity: T) => boolean;
19
+ orderBy?: keyof T & string;
20
+ order?: 'asc' | 'desc';
21
+ limit?: number;
22
+ offset?: number;
23
+ scope?: ModelScope;
24
+ }
25
+ export declare class QueryView<T extends Record<string, unknown>> implements IncrementalView {
26
+ /** The full (unlimited) internal result set, kept sorted. */
27
+ private _internal;
28
+ /**
29
+ * Public observable result set — windowed by offset/limit.
30
+ * Components observe this directly.
31
+ */
32
+ readonly results: IObservableArray<T>;
33
+ private readonly typename;
34
+ private readonly pool;
35
+ private readonly registry;
36
+ private readonly whereEntries;
37
+ private readonly filterFn;
38
+ private readonly sortKey;
39
+ private readonly sortDir;
40
+ private readonly limitN;
41
+ private readonly offsetN;
42
+ private readonly scope;
43
+ /** FK-index optimization: if the where clause targets a single FK-indexed field. */
44
+ private readonly fkField;
45
+ private readonly fkValue;
46
+ private disposed;
47
+ constructor(typename: string, pool: ObjectPool, registry: ViewRegistry, options?: QueryViewOptions<T>);
48
+ private initialScan;
49
+ handleAdded(model: Record<string, unknown>): void;
50
+ handleUpdated(model: Record<string, unknown>): void;
51
+ handleRemoved(modelId: string): void;
52
+ dispose(): void;
53
+ /** Check whether an entity passes both `where` and `filter`. */
54
+ private matchesFilter;
55
+ /** Insert entity into _internal at the correct sorted position. */
56
+ private insertSorted;
57
+ /** Find index of entity by id in _internal. */
58
+ private findIndexById;
59
+ /**
60
+ * Synchronize the public `results` array with the windowed slice of
61
+ * `_internal`. Mutates in place to keep the reference stable.
62
+ */
63
+ private syncWindow;
64
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * QueryView<T> — Incrementally maintained materialized view for a single query.
3
+ *
4
+ * Instead of scanning the full collection on every render, a QueryView
5
+ * builds the result set once, then maintains it via handleAdded /
6
+ * handleUpdated / handleRemoved calls from the ViewRegistry.
7
+ *
8
+ * `results` is a stable MobX observable array — components observe it
9
+ * directly and receive granular updates (splice/push, never replacement).
10
+ */
11
+ import { observable, runInAction } from 'mobx';
12
+ import { modelAsRow } from '../Model.js';
13
+ import { ModelScope } from '../types/index.js';
14
+ import { compareValues, binaryInsertionIndex, findIndexById, } from './query-utils.js';
15
+ // ---------------------------------------------------------------------------
16
+ // QueryView
17
+ // ---------------------------------------------------------------------------
18
+ export class QueryView {
19
+ /** The full (unlimited) internal result set, kept sorted. */
20
+ _internal;
21
+ /**
22
+ * Public observable result set — windowed by offset/limit.
23
+ * Components observe this directly.
24
+ */
25
+ results;
26
+ typename;
27
+ pool;
28
+ registry;
29
+ whereEntries;
30
+ filterFn;
31
+ sortKey;
32
+ sortDir;
33
+ limitN;
34
+ offsetN;
35
+ scope;
36
+ /** FK-index optimization: if the where clause targets a single FK-indexed field. */
37
+ fkField;
38
+ fkValue;
39
+ disposed = false;
40
+ constructor(typename, pool, registry, options = {}) {
41
+ this.typename = typename;
42
+ this.pool = pool;
43
+ this.registry = registry;
44
+ // Parse options
45
+ this.whereEntries = options.where
46
+ ? Object.entries(options.where).filter(([, v]) => v !== undefined)
47
+ : null;
48
+ this.filterFn = options.filter ?? null;
49
+ this.sortKey = options.orderBy ?? null;
50
+ this.sortDir = options.order === 'desc' ? -1 : 1;
51
+ this.limitN = options.limit;
52
+ this.offsetN = options.offset ?? 0;
53
+ this.scope = options.scope ?? ModelScope.live;
54
+ // Check for FK-index optimization: single-field where with an indexed FK
55
+ this.fkField = null;
56
+ this.fkValue = null;
57
+ if (this.whereEntries &&
58
+ this.whereEntries.length === 1 &&
59
+ typeof this.whereEntries[0][1] === 'string') {
60
+ const [field, value] = this.whereEntries[0];
61
+ if (pool.hasForeignKeyIndex(typename, field)) {
62
+ this.fkField = field;
63
+ this.fkValue = value;
64
+ }
65
+ }
66
+ // Create observable arrays (shallow — models are already observable)
67
+ this._internal = observable.array([], { deep: false });
68
+ this.results = observable.array([], { deep: false });
69
+ // Perform initial scan
70
+ this.initialScan();
71
+ // Register for incremental updates
72
+ this.registry.register(typename, this);
73
+ }
74
+ // -----------------------------------------------------------------------
75
+ // Initial scan
76
+ // -----------------------------------------------------------------------
77
+ initialScan() {
78
+ let candidates;
79
+ if (this.fkField && this.fkValue) {
80
+ // O(1) FK-index lookup
81
+ candidates = this.pool.getByForeignKey(this.typename, this.fkField, this.fkValue);
82
+ }
83
+ else {
84
+ candidates = this.pool.getByTypeName(this.typename, this.scope);
85
+ }
86
+ const matching = [];
87
+ for (const model of candidates) {
88
+ const entity = modelAsRow(model);
89
+ if (this.matchesFilter(entity)) {
90
+ matching.push(entity);
91
+ }
92
+ }
93
+ // Sort
94
+ if (this.sortKey) {
95
+ const key = this.sortKey;
96
+ const dir = this.sortDir;
97
+ matching.sort((a, b) => compareValues(a[key], b[key], dir));
98
+ }
99
+ runInAction(() => {
100
+ this._internal.replace(matching);
101
+ this.syncWindow();
102
+ });
103
+ }
104
+ // -----------------------------------------------------------------------
105
+ // Incremental update handlers (called by ViewRegistry)
106
+ // -----------------------------------------------------------------------
107
+ handleAdded(model) {
108
+ if (this.disposed)
109
+ return;
110
+ const entity = model;
111
+ const passesFilter = this.matchesFilter(entity);
112
+ if (!passesFilter)
113
+ return;
114
+ runInAction(() => {
115
+ this.insertSorted(entity);
116
+ this.syncWindow();
117
+ });
118
+ }
119
+ handleUpdated(model) {
120
+ if (this.disposed)
121
+ return;
122
+ const entity = model;
123
+ const id = entity['id'];
124
+ const idx = id !== undefined ? this.findIndexById(id) : -1;
125
+ const matchesNow = this.matchesFilter(entity);
126
+ runInAction(() => {
127
+ if (idx >= 0 && matchesNow) {
128
+ // Was in view and still matches.
129
+ // Models are plain objects (not MobX-observable), so we must notify
130
+ // observers that the data changed. Splice-in-place triggers the
131
+ // observable array to fire, which causes useQuery consumers to
132
+ // re-render with fresh property values.
133
+ this._internal.splice(idx, 1);
134
+ this.insertSorted(entity);
135
+ }
136
+ else if (idx >= 0 && !matchesNow) {
137
+ // Was in view but no longer matches — remove
138
+ this._internal.splice(idx, 1);
139
+ }
140
+ else if (idx < 0 && matchesNow) {
141
+ // Wasn't in view but now matches — add
142
+ this.insertSorted(entity);
143
+ }
144
+ this.syncWindow();
145
+ });
146
+ }
147
+ handleRemoved(modelId) {
148
+ if (this.disposed)
149
+ return;
150
+ const idx = this.findIndexById(modelId);
151
+ if (idx < 0)
152
+ return;
153
+ runInAction(() => {
154
+ this._internal.splice(idx, 1);
155
+ this.syncWindow();
156
+ });
157
+ }
158
+ // -----------------------------------------------------------------------
159
+ // Dispose
160
+ // -----------------------------------------------------------------------
161
+ dispose() {
162
+ if (this.disposed)
163
+ return;
164
+ this.disposed = true;
165
+ this.registry.unregister(this.typename, this);
166
+ }
167
+ // -----------------------------------------------------------------------
168
+ // Private helpers
169
+ // -----------------------------------------------------------------------
170
+ /** Check whether an entity passes both `where` and `filter`. */
171
+ matchesFilter(entity) {
172
+ // Note: scope is tracked per-entry in the ObjectPool, not on the model.
173
+ // The initial scan handles scope via getByTypeName(typename, scope).
174
+ // Incremental notifications from the pool are scope-appropriate since
175
+ // add/upsert/remove reflect the pool's authoritative scope tracking.
176
+ // Where clause: declarative field matching
177
+ if (this.whereEntries) {
178
+ for (const [key, value] of this.whereEntries) {
179
+ if (entity[key] !== value)
180
+ return false;
181
+ }
182
+ }
183
+ // Arbitrary predicate
184
+ if (this.filterFn) {
185
+ if (!this.filterFn(entity))
186
+ return false;
187
+ }
188
+ return true;
189
+ }
190
+ /** Insert entity into _internal at the correct sorted position. */
191
+ insertSorted(entity) {
192
+ if (this.sortKey) {
193
+ const idx = binaryInsertionIndex(this._internal, entity, this.sortKey, this.sortDir);
194
+ this._internal.splice(idx, 0, entity);
195
+ }
196
+ else {
197
+ this._internal.push(entity);
198
+ }
199
+ }
200
+ /** Find index of entity by id in _internal. */
201
+ findIndexById(id) {
202
+ return findIndexById(this._internal, id);
203
+ }
204
+ /**
205
+ * Synchronize the public `results` array with the windowed slice of
206
+ * `_internal`. Mutates in place to keep the reference stable.
207
+ */
208
+ syncWindow() {
209
+ const start = this.offsetN;
210
+ const end = this.limitN !== undefined ? start + this.limitN : this._internal.length;
211
+ const windowed = this._internal.slice(start, end);
212
+ // Minimal diff: only splice if contents differ
213
+ if (windowed.length === this.results.length &&
214
+ windowed.every((item, i) => this.results[i] === item)) {
215
+ return; // no change
216
+ }
217
+ this.results.replace(windowed);
218
+ }
219
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Linear Sync Engine - Store Manager
3
+ *
4
+ * Manages all ObjectStore instances for registered models.
5
+ * Creates appropriate store types based on model load strategies.
6
+ * Follows Linear's architecture with 80+ ObjectStore instances.
7
+ */
8
+ import { ModelRegistry } from '../ModelRegistry.js';
9
+ import { ObjectStore } from '../stores/ObjectStore.js';
10
+ import { SyncActionStore } from '../stores/SyncActionStore.js';
11
+ import { LoadStrategy } from '../types/index.js';
12
+ /**
13
+ * StoreManager - Central manager for all ObjectStore instances
14
+ *
15
+ * Key responsibilities:
16
+ * - Creates ObjectStore instances for each registered model
17
+ * - Manages store lifecycle and readiness
18
+ * - Provides unified interface for database operations
19
+ * - Handles store-specific optimizations based on load strategies
20
+ */
21
+ export declare class StoreManager {
22
+ private stores;
23
+ private syncactionStore;
24
+ private db;
25
+ private isInitialized;
26
+ private modelRegistry;
27
+ constructor(modelRegistry: ModelRegistry);
28
+ /**
29
+ * Initialize all stores for registered models
30
+ */
31
+ initializeStores(db: IDBDatabase): Promise<void>;
32
+ /**
33
+ * Create ObjectStore for a specific model
34
+ */
35
+ private createStoreForModel;
36
+ /**
37
+ * Create stores (tables) in IndexedDB
38
+ */
39
+ createStores(db: IDBDatabase, transaction: IDBTransaction): Promise<void>;
40
+ /**
41
+ * Create special tables (sync_action_table, model_table, model_table_partial, __meta, __transactions)
42
+ */
43
+ private createSpecialTables;
44
+ /**
45
+ * Get ObjectStore for a model
46
+ */
47
+ getStore(modelName: string): ObjectStore | undefined;
48
+ /**
49
+ * Get SyncactionStore instance
50
+ */
51
+ getSyncactionStore(): SyncActionStore | null;
52
+ /**
53
+ * Get all stores
54
+ */
55
+ getAllStores(): Map<string, ObjectStore>;
56
+ /**
57
+ * Check readiness of all stores
58
+ */
59
+ checkReadinessOfStores(): Promise<{
60
+ ready: boolean;
61
+ readyStores: string[];
62
+ notReadyStores: string[];
63
+ totalStores: number;
64
+ }>;
65
+ /**
66
+ * Check if ANY data store has at least one record.
67
+ *
68
+ * This is the Zero-style cache-validity check: if the stores are empty,
69
+ * the sync cursor (lastSyncId) is invalid regardless of what the metadata
70
+ * says. The cursor and the data must be co-located — no data means no
71
+ * cursor, which means full bootstrap.
72
+ *
73
+ * Samples up to 3 stores to avoid a full scan. If any store has records,
74
+ * returns true (we have cached data worth preserving).
75
+ */
76
+ hasAnyData(): Promise<boolean>;
77
+ /**
78
+ * Get store type distribution for debugging
79
+ */
80
+ getStoreTypeDistribution(): {
81
+ full: number;
82
+ partial: number;
83
+ };
84
+ /**
85
+ * Get stores by load strategy
86
+ */
87
+ getStoresByStrategy(strategy: LoadStrategy): ObjectStore[];
88
+ /**
89
+ * Get models to load for bootstrapping
90
+ */
91
+ getModelsToLoad(): {
92
+ instant: string[];
93
+ lazy: string[];
94
+ partial: string[];
95
+ };
96
+ /**
97
+ * Perform maintenance on all stores
98
+ */
99
+ performMaintenance(): Promise<void>;
100
+ /**
101
+ * Clear all stores
102
+ */
103
+ clearAllStores(): Promise<void>;
104
+ /**
105
+ * Mark all stores as closing to prevent new operations
106
+ * Called before database connection is closed
107
+ */
108
+ markAllStoresAsClosing(): void;
109
+ /**
110
+ * Get comprehensive statistics
111
+ */
112
+ getComprehensiveStats(): Promise<{
113
+ totalStores: number;
114
+ storeTypes: {
115
+ full: number;
116
+ partial: number;
117
+ };
118
+ readiness: {
119
+ ready: number;
120
+ notReady: number;
121
+ };
122
+ totalRecords: number;
123
+ storeDetails: Array<{
124
+ modelName: string;
125
+ storeName: string;
126
+ strategy: LoadStrategy;
127
+ ready: boolean;
128
+ count: number;
129
+ }>;
130
+ }>;
131
+ }