@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,53 @@
1
+ /**
2
+ * Bootstrap response factories for sync engine tests.
3
+ *
4
+ * Creates well-formed bootstrap responses matching the server API.
5
+ */
6
+ /**
7
+ * Create a full bootstrap response (fresh snapshot from server).
8
+ */
9
+ export function createFullBootstrapResponse(models, lastSyncId = 100) {
10
+ return {
11
+ type: 'full',
12
+ lastSyncId,
13
+ models,
14
+ timestamp: Date.now(),
15
+ };
16
+ }
17
+ /**
18
+ * Create a partial bootstrap response (delta batch from lastSyncId).
19
+ */
20
+ export function createPartialBootstrapResponse(deltas, lastSyncId) {
21
+ return {
22
+ type: 'partial',
23
+ lastSyncId,
24
+ deltas,
25
+ deltaCount: deltas?.length ?? 0,
26
+ timestamp: Date.now(),
27
+ };
28
+ }
29
+ /**
30
+ * Create a full bootstrap response with test model data pre-populated.
31
+ */
32
+ export function createTestBootstrapResponse(options = {}) {
33
+ const models = {};
34
+ if (options.tasks)
35
+ models.Task = options.tasks;
36
+ if (options.projects)
37
+ models.Project = options.projects;
38
+ if (options.slideDecks)
39
+ models.SlideDeck = options.slideDecks;
40
+ if (options.slides)
41
+ models.Slide = options.slides;
42
+ if (options.slideLayers)
43
+ models.SlideLayer = options.slideLayers;
44
+ if (options.comments)
45
+ models.Comment = options.comments;
46
+ return {
47
+ type: 'full',
48
+ lastSyncId: options.lastSyncId ?? 100,
49
+ models,
50
+ failedModels: options.failedModels,
51
+ timestamp: Date.now(),
52
+ };
53
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Delta factories for sync engine tests.
3
+ *
4
+ * Creates well-formed SyncAction objects matching the server wire format.
5
+ */
6
+ import type { SyncActionType, SyncAction } from '../../types/index.js';
7
+ /** Reset the delta counter (call in beforeEach for deterministic IDs) */
8
+ export declare function resetDeltaCounter(): void;
9
+ export interface CreateDeltaOptions {
10
+ /** Sync ID (auto-increments if not provided) */
11
+ id?: number;
12
+ /** Model name (e.g., 'Task', 'Slide') */
13
+ modelName: string;
14
+ /** Model ID */
15
+ modelId: string;
16
+ /** Action type: I=Insert, U=Update, D=Delete, A=Archive, V=Unarchive */
17
+ action: SyncActionType;
18
+ /** Delta payload data */
19
+ data?: Record<string, unknown>;
20
+ }
21
+ /**
22
+ * Create a single SyncAction (delta) matching the server wire format.
23
+ */
24
+ export declare function createDelta(options: CreateDeltaOptions): SyncAction;
25
+ /**
26
+ * Create an INSERT delta for a new entity.
27
+ */
28
+ export declare function createInsertDelta(modelName: string, modelId: string, data: Record<string, unknown>, syncId?: number): SyncAction;
29
+ /**
30
+ * Create an UPDATE delta for an existing entity.
31
+ */
32
+ export declare function createUpdateDelta(modelName: string, modelId: string, data: Record<string, unknown>, syncId?: number): SyncAction;
33
+ /**
34
+ * Create a DELETE delta.
35
+ */
36
+ export declare function createDeleteDelta(modelName: string, modelId: string, syncId?: number): SyncAction;
37
+ /**
38
+ * Create an ARCHIVE delta.
39
+ */
40
+ export declare function createArchiveDelta(modelName: string, modelId: string, syncId?: number): SyncAction;
41
+ /**
42
+ * Create an UNARCHIVE (reVive) delta.
43
+ */
44
+ export declare function createUnarchiveDelta(modelName: string, modelId: string, syncId?: number): SyncAction;
45
+ /**
46
+ * Create a COVERING ('C') delta.
47
+ *
48
+ * Signals that the client has gained permission to see an existing entity.
49
+ * Treated as an insert by the client — the entity is added to the local
50
+ * store as if newly created. Typically follows a GroupAdded delta.
51
+ */
52
+ export declare function createCoveringDelta(modelName: string, modelId: string, data: Record<string, unknown>, syncId?: number): SyncAction;
53
+ /**
54
+ * Create a GROUP ADDED ('G') delta using the incremental payload shape.
55
+ *
56
+ * Signals that the recipient was added to a single sync group. The client
57
+ * updates its subscription metadata and waits for Covering deltas to
58
+ * deliver the newly-visible entities. Unlike the legacy 'G' payload
59
+ * (addedGroups/removedGroups), this does not trigger a re-bootstrap.
60
+ */
61
+ export declare function createGroupAddedDelta(userId: string, group: string, syncId?: number): SyncAction;
62
+ /**
63
+ * Create a legacy GROUP CHANGE ('G') delta with the old payload shape.
64
+ *
65
+ * Carries both added and removed groups in one delta and forces a full
66
+ * re-bootstrap on the client. Use for testing backward compatibility with
67
+ * the deprecated EmitGroupChange path.
68
+ */
69
+ export declare function createLegacyGroupChangeDelta(userId: string, added: string[], removed: string[], syncId?: number): SyncAction;
70
+ /**
71
+ * Create a GROUP REMOVED ('S') delta.
72
+ *
73
+ * Signals that the recipient lost access to a sync group. The client
74
+ * purges affected local state and triggers a re-bootstrap with the
75
+ * updated group list.
76
+ */
77
+ export declare function createGroupRemovedDelta(userId: string, group: string, syncId?: number): SyncAction;
78
+ /**
79
+ * Create a batch of deltas with sequential sync IDs.
80
+ */
81
+ export declare function createDeltaBatch(deltas: Array<Omit<CreateDeltaOptions, 'id'>>, startingSyncId?: number): SyncAction[];
82
+ /**
83
+ * Create a confirmation delta — used to confirm that a mutation
84
+ * was persisted by the server (TransactionQueue watches for this).
85
+ */
86
+ export declare function createConfirmationDelta(modelName: string, modelId: string, syncId: number, action?: SyncActionType, data?: Record<string, unknown>): SyncAction;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Delta factories for sync engine tests.
3
+ *
4
+ * Creates well-formed SyncAction objects matching the server wire format.
5
+ */
6
+ let deltaCounter = 0;
7
+ /** Reset the delta counter (call in beforeEach for deterministic IDs) */
8
+ export function resetDeltaCounter() {
9
+ deltaCounter = 0;
10
+ }
11
+ /**
12
+ * Create a single SyncAction (delta) matching the server wire format.
13
+ */
14
+ export function createDelta(options) {
15
+ deltaCounter++;
16
+ return {
17
+ id: options.id ?? deltaCounter,
18
+ modelName: options.modelName,
19
+ modelId: options.modelId,
20
+ action: options.action,
21
+ data: options.data ?? {},
22
+ __class: 'SyncAction',
23
+ };
24
+ }
25
+ /**
26
+ * Create an INSERT delta for a new entity.
27
+ */
28
+ export function createInsertDelta(modelName, modelId, data, syncId) {
29
+ return createDelta({ modelName, modelId, action: 'I', data, id: syncId });
30
+ }
31
+ /**
32
+ * Create an UPDATE delta for an existing entity.
33
+ */
34
+ export function createUpdateDelta(modelName, modelId, data, syncId) {
35
+ return createDelta({ modelName, modelId, action: 'U', data, id: syncId });
36
+ }
37
+ /**
38
+ * Create a DELETE delta.
39
+ */
40
+ export function createDeleteDelta(modelName, modelId, syncId) {
41
+ return createDelta({ modelName, modelId, action: 'D', data: {}, id: syncId });
42
+ }
43
+ /**
44
+ * Create an ARCHIVE delta.
45
+ */
46
+ export function createArchiveDelta(modelName, modelId, syncId) {
47
+ return createDelta({
48
+ modelName,
49
+ modelId,
50
+ action: 'A',
51
+ data: { archivedAt: new Date().toISOString() },
52
+ id: syncId,
53
+ });
54
+ }
55
+ /**
56
+ * Create an UNARCHIVE (reVive) delta.
57
+ */
58
+ export function createUnarchiveDelta(modelName, modelId, syncId) {
59
+ return createDelta({
60
+ modelName,
61
+ modelId,
62
+ action: 'V',
63
+ data: { archivedAt: null },
64
+ id: syncId,
65
+ });
66
+ }
67
+ /**
68
+ * Create a COVERING ('C') delta.
69
+ *
70
+ * Signals that the client has gained permission to see an existing entity.
71
+ * Treated as an insert by the client — the entity is added to the local
72
+ * store as if newly created. Typically follows a GroupAdded delta.
73
+ */
74
+ export function createCoveringDelta(modelName, modelId, data, syncId) {
75
+ return createDelta({ modelName, modelId, action: 'C', data, id: syncId });
76
+ }
77
+ /**
78
+ * Create a GROUP ADDED ('G') delta using the incremental payload shape.
79
+ *
80
+ * Signals that the recipient was added to a single sync group. The client
81
+ * updates its subscription metadata and waits for Covering deltas to
82
+ * deliver the newly-visible entities. Unlike the legacy 'G' payload
83
+ * (addedGroups/removedGroups), this does not trigger a re-bootstrap.
84
+ */
85
+ export function createGroupAddedDelta(userId, group, syncId) {
86
+ return createDelta({
87
+ modelName: 'SyncGroupChange',
88
+ modelId: `sga_${userId}`,
89
+ action: 'G',
90
+ data: { group, userId },
91
+ id: syncId,
92
+ });
93
+ }
94
+ /**
95
+ * Create a legacy GROUP CHANGE ('G') delta with the old payload shape.
96
+ *
97
+ * Carries both added and removed groups in one delta and forces a full
98
+ * re-bootstrap on the client. Use for testing backward compatibility with
99
+ * the deprecated EmitGroupChange path.
100
+ */
101
+ export function createLegacyGroupChangeDelta(userId, added, removed, syncId) {
102
+ return createDelta({
103
+ modelName: 'SyncGroupChange',
104
+ modelId: `sgc_${userId}`,
105
+ action: 'G',
106
+ data: { addedGroups: added, removedGroups: removed },
107
+ id: syncId,
108
+ });
109
+ }
110
+ /**
111
+ * Create a GROUP REMOVED ('S') delta.
112
+ *
113
+ * Signals that the recipient lost access to a sync group. The client
114
+ * purges affected local state and triggers a re-bootstrap with the
115
+ * updated group list.
116
+ */
117
+ export function createGroupRemovedDelta(userId, group, syncId) {
118
+ return createDelta({
119
+ modelName: 'SyncGroupChange',
120
+ modelId: `sgr_${userId}`,
121
+ action: 'S',
122
+ data: { group, userId },
123
+ id: syncId,
124
+ });
125
+ }
126
+ /**
127
+ * Create a batch of deltas with sequential sync IDs.
128
+ */
129
+ export function createDeltaBatch(deltas, startingSyncId) {
130
+ const start = startingSyncId ?? deltaCounter + 1;
131
+ return deltas.map((d, i) => createDelta({ ...d, id: start + i }));
132
+ }
133
+ /**
134
+ * Create a confirmation delta — used to confirm that a mutation
135
+ * was persisted by the server (TransactionQueue watches for this).
136
+ */
137
+ export function createConfirmationDelta(modelName, modelId, syncId, action = 'U', data = {}) {
138
+ return createDelta({ modelName, modelId, action, data, id: syncId });
139
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Test Model subclasses for @ablo/sync-engine tests.
3
+ *
4
+ * Lightweight Model implementations with FK relationships matching
5
+ * the MODEL_CREATE_PRIORITY map in TransactionQueue:
6
+ * TestProject (10) → TestTask (10, FK→Project) → TestComment (30, FK→Task)
7
+ * TestSlideDeck (10) → TestSlide (15, FK→SlideDeck) → TestSlideLayer (20, FK→Slide)
8
+ */
9
+ import { Model } from '../../Model.js';
10
+ import { ModelRegistry } from '../../ModelRegistry.js';
11
+ export declare class TestProject extends Model {
12
+ name: string;
13
+ description: string;
14
+ organizationId: string;
15
+ constructor(data?: Partial<TestProject> & Record<string, unknown>);
16
+ getModelName(): string;
17
+ }
18
+ export declare class TestTask extends Model {
19
+ title: string;
20
+ status: string;
21
+ projectId: string | null;
22
+ assigneeId: string | null;
23
+ organizationId: string;
24
+ constructor(data?: Partial<TestTask> & Record<string, unknown>);
25
+ getModelName(): string;
26
+ }
27
+ export declare class TestComment extends Model {
28
+ body: string;
29
+ taskId: string | null;
30
+ organizationId: string;
31
+ constructor(data?: Partial<TestComment> & Record<string, unknown>);
32
+ getModelName(): string;
33
+ }
34
+ export declare class TestSlideDeck extends Model {
35
+ title: string;
36
+ organizationId: string;
37
+ constructor(data?: Partial<TestSlideDeck> & Record<string, unknown>);
38
+ getModelName(): string;
39
+ }
40
+ export declare class TestSlide extends Model {
41
+ order: number;
42
+ deckId: string | null;
43
+ organizationId: string;
44
+ constructor(data?: Partial<TestSlide> & Record<string, unknown>);
45
+ getModelName(): string;
46
+ }
47
+ export declare class TestSlideLayer extends Model {
48
+ slideId: string | null;
49
+ zIndex: number;
50
+ type: string;
51
+ content: string;
52
+ organizationId: string;
53
+ constructor(data?: Partial<TestSlideLayer> & Record<string, unknown>);
54
+ getModelName(): string;
55
+ }
56
+ /**
57
+ * Model → priority mapping matching TransactionQueue's MODEL_CREATE_PRIORITY.
58
+ */
59
+ export declare const TEST_MODEL_PRIORITIES: Map<string, number>;
60
+ /**
61
+ * Register all test models with a ModelRegistry instance.
62
+ * Sets up properties, references, and FK relationships.
63
+ */
64
+ export declare function registerTestModels(registry: ModelRegistry): void;
65
+ /**
66
+ * Create a SyncEngineConfig pre-configured with test model priorities.
67
+ */
68
+ export declare function createTestConfig(): {
69
+ modelCreatePriority: ReadonlyMap<string, number>;
70
+ defaultCreatePriority: number;
71
+ defaultNonCreatePriority: number;
72
+ essentialFields: Readonly<Record<string, readonly string[]>>;
73
+ classNameFallbackMap: Readonly<Record<string, string>>;
74
+ };
75
+ /** Reset the counter (call in beforeEach for deterministic IDs) */
76
+ export declare function resetFixtureCounter(): void;
77
+ export declare function createProjectFixture(overrides?: Partial<Record<string, unknown>>): TestProject;
78
+ export declare function createTaskFixture(overrides?: Partial<Record<string, unknown>>): TestTask;
79
+ export declare function createCommentFixture(overrides?: Partial<Record<string, unknown>>): TestComment;
80
+ export declare function createSlideDeckFixture(overrides?: Partial<Record<string, unknown>>): TestSlideDeck;
81
+ export declare function createSlideFixture(overrides?: Partial<Record<string, unknown>>): TestSlide;
82
+ export declare function createSlideLayerFixture(overrides?: Partial<Record<string, unknown>>): TestSlideLayer;
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Test Model subclasses for @ablo/sync-engine tests.
3
+ *
4
+ * Lightweight Model implementations with FK relationships matching
5
+ * the MODEL_CREATE_PRIORITY map in TransactionQueue:
6
+ * TestProject (10) → TestTask (10, FK→Project) → TestComment (30, FK→Task)
7
+ * TestSlideDeck (10) → TestSlide (15, FK→SlideDeck) → TestSlideLayer (20, FK→Slide)
8
+ */
9
+ import { Model } from '../../Model.js';
10
+ import { PropertyType, LoadStrategy } from '../../types/index.js';
11
+ // ─────────────────────────────────────────────
12
+ // Test Model Classes
13
+ // ─────────────────────────────────────────────
14
+ export class TestProject extends Model {
15
+ name = '';
16
+ description = '';
17
+ organizationId = 'test-org';
18
+ constructor(data = {}) {
19
+ super(data);
20
+ if (data.name != null)
21
+ this.name = data.name;
22
+ if (data.description != null)
23
+ this.description = data.description;
24
+ if (data.organizationId != null)
25
+ this.organizationId = data.organizationId;
26
+ }
27
+ getModelName() {
28
+ return 'Project';
29
+ }
30
+ }
31
+ export class TestTask extends Model {
32
+ title = '';
33
+ status = 'todo';
34
+ projectId = null;
35
+ assigneeId = null;
36
+ organizationId = 'test-org';
37
+ constructor(data = {}) {
38
+ super(data);
39
+ if (data.title != null)
40
+ this.title = data.title;
41
+ if (data.status != null)
42
+ this.status = data.status;
43
+ if (data.projectId !== undefined)
44
+ this.projectId = data.projectId;
45
+ if (data.assigneeId !== undefined)
46
+ this.assigneeId = data.assigneeId;
47
+ if (data.organizationId != null)
48
+ this.organizationId = data.organizationId;
49
+ }
50
+ getModelName() {
51
+ return 'Task';
52
+ }
53
+ }
54
+ export class TestComment extends Model {
55
+ body = '';
56
+ taskId = null;
57
+ organizationId = 'test-org';
58
+ constructor(data = {}) {
59
+ super(data);
60
+ if (data.body != null)
61
+ this.body = data.body;
62
+ if (data.taskId !== undefined)
63
+ this.taskId = data.taskId;
64
+ if (data.organizationId != null)
65
+ this.organizationId = data.organizationId;
66
+ }
67
+ getModelName() {
68
+ return 'Comment';
69
+ }
70
+ }
71
+ export class TestSlideDeck extends Model {
72
+ title = '';
73
+ organizationId = 'test-org';
74
+ constructor(data = {}) {
75
+ super(data);
76
+ if (data.title != null)
77
+ this.title = data.title;
78
+ if (data.organizationId != null)
79
+ this.organizationId = data.organizationId;
80
+ }
81
+ getModelName() {
82
+ return 'SlideDeck';
83
+ }
84
+ }
85
+ export class TestSlide extends Model {
86
+ order = 0;
87
+ deckId = null;
88
+ organizationId = 'test-org';
89
+ constructor(data = {}) {
90
+ super(data);
91
+ if (data.order != null)
92
+ this.order = data.order;
93
+ if (data.deckId !== undefined)
94
+ this.deckId = data.deckId;
95
+ if (data.organizationId != null)
96
+ this.organizationId = data.organizationId;
97
+ }
98
+ getModelName() {
99
+ return 'Slide';
100
+ }
101
+ }
102
+ export class TestSlideLayer extends Model {
103
+ slideId = null;
104
+ zIndex = 0;
105
+ type = 'text';
106
+ content = '';
107
+ organizationId = 'test-org';
108
+ constructor(data = {}) {
109
+ super(data);
110
+ if (data.slideId !== undefined)
111
+ this.slideId = data.slideId;
112
+ if (data.zIndex != null)
113
+ this.zIndex = data.zIndex;
114
+ if (data.type != null)
115
+ this.type = data.type;
116
+ if (data.content != null)
117
+ this.content = data.content;
118
+ if (data.organizationId != null)
119
+ this.organizationId = data.organizationId;
120
+ }
121
+ getModelName() {
122
+ return 'SlideLayer';
123
+ }
124
+ }
125
+ // ─────────────────────────────────────────────
126
+ // Model Registration Helper
127
+ // ─────────────────────────────────────────────
128
+ /**
129
+ * Model → priority mapping matching TransactionQueue's MODEL_CREATE_PRIORITY.
130
+ */
131
+ export const TEST_MODEL_PRIORITIES = new Map([
132
+ ['Project', 10],
133
+ ['Task', 10],
134
+ ['SlideDeck', 10],
135
+ ['Slide', 15],
136
+ ['SlideLayer', 20],
137
+ ['Comment', 30],
138
+ ]);
139
+ /**
140
+ * Register all test models with a ModelRegistry instance.
141
+ * Sets up properties, references, and FK relationships.
142
+ */
143
+ export function registerTestModels(registry) {
144
+ registry.startBatch();
145
+ // Register model classes
146
+ registry.registerModel('Project', TestProject, { loadStrategy: LoadStrategy.instant });
147
+ registry.registerModel('Task', TestTask, { loadStrategy: LoadStrategy.instant });
148
+ registry.registerModel('Comment', TestComment, { loadStrategy: LoadStrategy.instant });
149
+ registry.registerModel('SlideDeck', TestSlideDeck, { loadStrategy: LoadStrategy.instant });
150
+ registry.registerModel('Slide', TestSlide, { loadStrategy: LoadStrategy.instant });
151
+ registry.registerModel('SlideLayer', TestSlideLayer, { loadStrategy: LoadStrategy.instant });
152
+ // Register properties
153
+ registry.registerProperty('Project', 'name', { type: PropertyType.property });
154
+ registry.registerProperty('Project', 'description', { type: PropertyType.property, optional: true });
155
+ registry.registerProperty('Project', 'organizationId', { type: PropertyType.property });
156
+ registry.registerProperty('Task', 'title', { type: PropertyType.property });
157
+ registry.registerProperty('Task', 'status', { type: PropertyType.property });
158
+ registry.registerProperty('Task', 'projectId', { type: PropertyType.reference, nullable: true });
159
+ registry.registerProperty('Task', 'assigneeId', { type: PropertyType.reference, nullable: true });
160
+ registry.registerProperty('Task', 'organizationId', { type: PropertyType.property });
161
+ registry.registerProperty('Comment', 'body', { type: PropertyType.property });
162
+ registry.registerProperty('Comment', 'taskId', { type: PropertyType.reference, nullable: true });
163
+ registry.registerProperty('Comment', 'organizationId', { type: PropertyType.property });
164
+ registry.registerProperty('SlideDeck', 'title', { type: PropertyType.property });
165
+ registry.registerProperty('SlideDeck', 'organizationId', { type: PropertyType.property });
166
+ registry.registerProperty('Slide', 'order', { type: PropertyType.property });
167
+ registry.registerProperty('Slide', 'deckId', { type: PropertyType.reference, nullable: true });
168
+ registry.registerProperty('Slide', 'organizationId', { type: PropertyType.property });
169
+ registry.registerProperty('SlideLayer', 'slideId', { type: PropertyType.reference, nullable: true });
170
+ registry.registerProperty('SlideLayer', 'zIndex', { type: PropertyType.property });
171
+ registry.registerProperty('SlideLayer', 'type', { type: PropertyType.property });
172
+ registry.registerProperty('SlideLayer', 'content', { type: PropertyType.property });
173
+ registry.registerProperty('SlideLayer', 'organizationId', { type: PropertyType.property });
174
+ // Register back-references for cascade-aware transaction handling
175
+ registry.registerBackReference('Task', { parentModel: 'Project', foreignKey: 'projectId', cascadeDelete: true });
176
+ registry.registerBackReference('Comment', { parentModel: 'Task', foreignKey: 'taskId', cascadeDelete: true });
177
+ registry.registerBackReference('Slide', { parentModel: 'SlideDeck', foreignKey: 'deckId', cascadeDelete: true });
178
+ registry.registerBackReference('SlideLayer', { parentModel: 'Slide', foreignKey: 'slideId', cascadeDelete: true });
179
+ registry.endBatch();
180
+ }
181
+ // ─────────────────────────────────────────────
182
+ // Test SyncEngineConfig factory
183
+ // ─────────────────────────────────────────────
184
+ /**
185
+ * Create a SyncEngineConfig pre-configured with test model priorities.
186
+ */
187
+ export function createTestConfig() {
188
+ return {
189
+ modelCreatePriority: TEST_MODEL_PRIORITIES,
190
+ defaultCreatePriority: 40,
191
+ defaultNonCreatePriority: 50,
192
+ essentialFields: {
193
+ Task: ['title', 'projectId'],
194
+ Slide: ['deckId', 'order'],
195
+ },
196
+ classNameFallbackMap: {
197
+ TestProject: 'Project',
198
+ TestTask: 'Task',
199
+ TestComment: 'Comment',
200
+ TestSlideDeck: 'SlideDeck',
201
+ TestSlide: 'Slide',
202
+ TestSlideLayer: 'SlideLayer',
203
+ },
204
+ };
205
+ }
206
+ // ─────────────────────────────────────────────
207
+ // Fixture factories
208
+ // ─────────────────────────────────────────────
209
+ let fixtureCounter = 0;
210
+ /** Reset the counter (call in beforeEach for deterministic IDs) */
211
+ export function resetFixtureCounter() {
212
+ fixtureCounter = 0;
213
+ }
214
+ export function createProjectFixture(overrides = {}) {
215
+ fixtureCounter++;
216
+ return new TestProject({
217
+ id: `project-${fixtureCounter}`,
218
+ name: `Test Project ${fixtureCounter}`,
219
+ organizationId: 'test-org',
220
+ ...overrides,
221
+ });
222
+ }
223
+ export function createTaskFixture(overrides = {}) {
224
+ fixtureCounter++;
225
+ return new TestTask({
226
+ id: `task-${fixtureCounter}`,
227
+ title: `Test Task ${fixtureCounter}`,
228
+ status: 'todo',
229
+ organizationId: 'test-org',
230
+ ...overrides,
231
+ });
232
+ }
233
+ export function createCommentFixture(overrides = {}) {
234
+ fixtureCounter++;
235
+ return new TestComment({
236
+ id: `comment-${fixtureCounter}`,
237
+ body: `Test comment ${fixtureCounter}`,
238
+ organizationId: 'test-org',
239
+ ...overrides,
240
+ });
241
+ }
242
+ export function createSlideDeckFixture(overrides = {}) {
243
+ fixtureCounter++;
244
+ return new TestSlideDeck({
245
+ id: `deck-${fixtureCounter}`,
246
+ title: `Test Deck ${fixtureCounter}`,
247
+ organizationId: 'test-org',
248
+ ...overrides,
249
+ });
250
+ }
251
+ export function createSlideFixture(overrides = {}) {
252
+ fixtureCounter++;
253
+ return new TestSlide({
254
+ id: `slide-${fixtureCounter}`,
255
+ order: fixtureCounter,
256
+ organizationId: 'test-org',
257
+ ...overrides,
258
+ });
259
+ }
260
+ export function createSlideLayerFixture(overrides = {}) {
261
+ fixtureCounter++;
262
+ return new TestSlideLayer({
263
+ id: `layer-${fixtureCounter}`,
264
+ zIndex: fixtureCounter,
265
+ type: 'text',
266
+ content: `Layer content ${fixtureCounter}`,
267
+ organizationId: 'test-org',
268
+ ...overrides,
269
+ });
270
+ }