@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,60 @@
1
+ /**
2
+ * query-utils — Pure query helpers shared between QueryView (MobX) and
3
+ * AgentQueryView (headless). One source of truth for sort, filter, and
4
+ * binary insertion logic.
5
+ *
6
+ * No MobX, no ObjectPool, no Model — just arrays and values.
7
+ */
8
+ /** Compare two values for sorting, null-safe. Returns -1 | 0 | 1. */
9
+ export function compareValues(a, b, dir) {
10
+ if (a === b)
11
+ return 0;
12
+ if (a == null)
13
+ return 1;
14
+ if (b == null)
15
+ return -1;
16
+ return (a < b ? -1 : 1) * dir;
17
+ }
18
+ /**
19
+ * Binary search for the correct insertion index in a sorted array.
20
+ * Returns the index at which `item` should be inserted.
21
+ */
22
+ export function binaryInsertionIndex(arr, item, sortKey, dir) {
23
+ let lo = 0;
24
+ let hi = arr.length;
25
+ const itemVal = item[sortKey];
26
+ while (lo < hi) {
27
+ const mid = (lo + hi) >>> 1;
28
+ const midVal = arr[mid][sortKey];
29
+ if (compareValues(midVal, itemVal, dir) <= 0) {
30
+ lo = mid + 1;
31
+ }
32
+ else {
33
+ hi = mid;
34
+ }
35
+ }
36
+ return lo;
37
+ }
38
+ /**
39
+ * Check whether an entity matches a declarative `where` clause.
40
+ * Every key in `where` must exactly match the entity's value.
41
+ */
42
+ export function matchesWhere(entity, where) {
43
+ for (const [key, value] of Object.entries(where)) {
44
+ if (value === undefined)
45
+ continue;
46
+ if (entity[key] !== value)
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+ /**
52
+ * Find the index of an entity by id in an array. Returns -1 if not found.
53
+ */
54
+ export function findIndexById(arr, id) {
55
+ for (let i = 0; i < arr.length; i++) {
56
+ if (arr[i]['id'] === id)
57
+ return i;
58
+ }
59
+ return -1;
60
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Typed error hierarchy for `@ablo/sync-engine`.
3
+ *
4
+ * Inlined directly so the publishable dist is self-contained. The public
5
+ * package should never reference an unpublished internal package from emitted
6
+ * JS; strict bundlers surface that immediately.
7
+ *
8
+ * ### Two patterns for consumers
9
+ *
10
+ * ```ts
11
+ * // Stripe-style instanceof
12
+ * if (err instanceof AbloRateLimitError) backoff(err.retryAfterSeconds);
13
+ *
14
+ * // Discriminator-string (for cross-boundary cases where the class
15
+ * // identity gets lost, e.g. across a web worker postMessage)
16
+ * if (err.type === 'AbloRateLimitError') { ... }
17
+ * ```
18
+ *
19
+ * Both work on every subclass.
20
+ */
21
+ /** Common shape for all errors thrown by this SDK. */
22
+ export declare class AbloError extends Error {
23
+ /** Discriminator string — matches the class name. Lets consumers
24
+ * switch on `e.type` without `instanceof` checks across package
25
+ * boundaries (matches Stripe's `err.type` pattern). */
26
+ readonly type: string;
27
+ /** Stable short identifier for logs + metrics.
28
+ * E.g. `'apikey_invalid'`, `'capability_scope_denied'`. */
29
+ readonly code?: string;
30
+ /** HTTP status code when the error originated from an HTTP response. */
31
+ readonly httpStatus?: number;
32
+ /** Correlation id for ops — present when the server sent one on
33
+ * `x-request-id`. Include in support tickets. */
34
+ readonly requestId?: string;
35
+ constructor(message: string, options?: {
36
+ code?: string;
37
+ httpStatus?: number;
38
+ requestId?: string;
39
+ cause?: unknown;
40
+ });
41
+ }
42
+ /** 401 — invalid/missing/expired credentials. */
43
+ export declare class AbloAuthenticationError extends AbloError {
44
+ readonly type: "AbloAuthenticationError";
45
+ }
46
+ /** 403 — credentials were valid but the action is forbidden (scope
47
+ * denial, revoked capability, role not authorized). */
48
+ export declare class AbloPermissionError extends AbloError {
49
+ readonly type: "AbloPermissionError";
50
+ }
51
+ /** 429 — rate limit exceeded. Consumers should back off before retry. */
52
+ export declare class AbloRateLimitError extends AbloError {
53
+ readonly type: "AbloRateLimitError";
54
+ readonly retryAfterSeconds?: number;
55
+ constructor(message: string, options?: {
56
+ code?: string;
57
+ httpStatus?: number;
58
+ requestId?: string;
59
+ cause?: unknown;
60
+ retryAfterSeconds?: number;
61
+ });
62
+ }
63
+ /** 409 — same `Idempotency-Key` reused with a different request body. */
64
+ export declare class AbloIdempotencyError extends AbloError {
65
+ readonly type: "AbloIdempotencyError";
66
+ }
67
+ /** Network / transport failure — TCP reset, DNS, timeout, abort. */
68
+ export declare class AbloConnectionError extends AbloError {
69
+ readonly type: "AbloConnectionError";
70
+ }
71
+ /** 400 / 422 — request payload was invalid. */
72
+ export declare class AbloValidationError extends AbloError {
73
+ readonly type: "AbloValidationError";
74
+ }
75
+ /** 5xx — server-side error. Usually retryable with backoff. */
76
+ export declare class AbloServerError extends AbloError {
77
+ readonly type: "AbloServerError";
78
+ }
79
+ /**
80
+ * 409 — a write carried `readAt: N` but the target entity has received
81
+ * deltas since `N`. The caller's reasoning snapshot is stale; the safe
82
+ * response is to re-read (or re-capture a watermark) and regenerate.
83
+ *
84
+ * Carries `conflicts` so callers can inspect which specific (model, id)
85
+ * pairs moved during the generation window — useful for metrics
86
+ * ("72% of stale rejects were on slide titles") and for selective
87
+ * regeneration (only re-think the slides that changed, not the whole
88
+ * deck).
89
+ */
90
+ export declare class AbloStaleContextError extends AbloError {
91
+ readonly type: "AbloStaleContextError";
92
+ /** Sync id at the caller's `readAt` when the write was attempted. */
93
+ readonly readAt?: number;
94
+ /** Entities that received deltas between `readAt` and the write. */
95
+ readonly conflicts?: ReadonlyArray<{
96
+ readonly model: string;
97
+ readonly id: string;
98
+ readonly observedSyncId: number;
99
+ }>;
100
+ constructor(message: string, options?: {
101
+ code?: string;
102
+ httpStatus?: number;
103
+ requestId?: string;
104
+ cause?: unknown;
105
+ readAt?: number;
106
+ conflicts?: ReadonlyArray<{
107
+ readonly model: string;
108
+ readonly id: string;
109
+ readonly observedSyncId: number;
110
+ }>;
111
+ });
112
+ }
113
+ /**
114
+ * The target entity currently has an active intent held by another
115
+ * participant and the caller asked the SDK not to return immediately.
116
+ *
117
+ * Use `ifBusy: 'wait'` to wait for the intent stream to clear, or
118
+ * `ifBusy: 'return'` to inspect the active intents yourself.
119
+ */
120
+ export declare class AbloBusyError extends AbloError {
121
+ readonly type: "AbloBusyError";
122
+ readonly intents?: ReadonlyArray<unknown>;
123
+ constructor(message: string, options?: {
124
+ code?: string;
125
+ httpStatus?: number;
126
+ requestId?: string;
127
+ cause?: unknown;
128
+ intents?: ReadonlyArray<unknown>;
129
+ });
130
+ }
131
+ /**
132
+ * Structured description of the capability an agent would need to
133
+ * satisfy a denied request. Mirrors the x402 `paymentRequirements`
134
+ * shape: the server emits enough information for the client to
135
+ * attenuate (or request) a capability that would pass on retry.
136
+ */
137
+ export interface RequiredCapability {
138
+ /** Operation or capability scope identifier (e.g. `"slide.update"`,
139
+ * `"subscribe"`). */
140
+ readonly scope: string;
141
+ /** Concrete constraints the capability must satisfy. Keys map to
142
+ * Datalog fact families — e.g. `{ syncGroup: ["org_abc"] }` for a
143
+ * rejected subscription. Forward-compatible: ignore unknown keys. */
144
+ readonly constraints?: Readonly<Record<string, readonly string[] | string>>;
145
+ /** Issuer hint — public-key fingerprint or well-known URL fragment. */
146
+ readonly issuer?: string;
147
+ /** Server-suggested maximum TTL for the attenuated capability. */
148
+ readonly ttlSeconds?: number;
149
+ /** Single-use nonce to embed in the retry's capability facts; binds
150
+ * retry → denial and prevents replay of a stale attenuation. */
151
+ readonly nonce?: string;
152
+ }
153
+ /**
154
+ * Canonical receipt returned by every successful or rejected commit,
155
+ * regardless of transport (WebSocket `mutation_result` payload, HTTP
156
+ * `/v1/commits` response body, or `AgentJob.result.receipt`). One
157
+ * shape across all three surfaces.
158
+ *
159
+ * Linear-style correlation receipt — no cryptographic signature
160
+ * (single-tenant trust boundary). A Hub signature is a forward-
161
+ * compatible extension when cross-org agent crossings arrive.
162
+ */
163
+ export interface CommitReceipt {
164
+ readonly object: 'commit_receipt';
165
+ /** Client-issued idempotency handle. Echoed verbatim. */
166
+ readonly clientTxId: string;
167
+ /** Server-issued opaque commit id — typically `String(lastSyncId)`. */
168
+ readonly serverTxId: string;
169
+ /** Convenience boolean. `status === 'confirmed'`. */
170
+ readonly success: boolean;
171
+ /** `'confirmed'` on apply, `'rejected'` on any failure. */
172
+ readonly status: 'confirmed' | 'rejected';
173
+ /** Last syncId visible after this commit (or high-water mark on
174
+ * rejection). */
175
+ readonly lastSyncId?: number;
176
+ /** Number of operations metered. Reported on both success and
177
+ * rejection so quota systems see attempted work. */
178
+ readonly ops?: number;
179
+ /** Populated on rejection. `requiredCapability` (when present)
180
+ * carries the x402-style structured retry hint. */
181
+ readonly error?: {
182
+ readonly code: string;
183
+ readonly message: string;
184
+ readonly field?: string;
185
+ readonly requiredCapability?: RequiredCapability;
186
+ };
187
+ }
188
+ /**
189
+ * Biscuit capability token failed verification — either it's
190
+ * malformed / unknown / revoked (`capability_invalid`), or its
191
+ * caveats deny the attempted action (`capability_scope_denied`).
192
+ *
193
+ * Extends `AbloPermissionError` so existing `instanceof CapabilityError`
194
+ * checks keep working AND broader `instanceof AbloPermissionError`
195
+ * matches for consumers who don't care about the Biscuit specifics.
196
+ *
197
+ * `requiredCapability` (when present) describes what an attenuated
198
+ * capability must satisfy for the request to succeed on retry.
199
+ */
200
+ export declare class CapabilityError extends AbloPermissionError {
201
+ readonly requiredCapability?: RequiredCapability;
202
+ constructor(code: 'capability_scope_denied' | 'capability_invalid', message: string, requiredCapability?: RequiredCapability);
203
+ }
204
+ /**
205
+ * SyncSessionError — Thrown when authentication/session is invalid or expired.
206
+ * Signals that the user should be redirected to sign in
207
+ * rather than showing a generic retry option.
208
+ *
209
+ * Extends `AbloAuthenticationError` so existing
210
+ * `SyncSessionError.isSessionError(...)` duck-type callers keep
211
+ * working, AND downstream code that only catches the typed hierarchy
212
+ * (`instanceof AbloAuthenticationError` / `e.type === 'AbloAuthenticationError'`)
213
+ * now sees session failures too.
214
+ */
215
+ export declare class SyncSessionError extends AbloAuthenticationError {
216
+ readonly isSessionError = true;
217
+ readonly statusCode: number;
218
+ constructor(message: string, statusCode?: number);
219
+ /**
220
+ * Check if an error is a session error (duck-type check)
221
+ */
222
+ static isSessionError(error: unknown): error is SyncSessionError;
223
+ /**
224
+ * Check if an HTTP response status indicates a session error
225
+ */
226
+ static isSessionErrorResponse(status: number, body?: string): boolean;
227
+ }
228
+ /**
229
+ * Translate an HTTP response into the appropriate typed error.
230
+ *
231
+ * Single source of truth for status-code → class mapping — every SDK
232
+ * fetch path that sees a non-2xx response should route through here
233
+ * so the customer-visible error is always the right subclass.
234
+ */
235
+ export declare function translateHttpError(status: number, body: unknown, requestId?: string): AbloError;
package/dist/errors.js ADDED
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Typed error hierarchy for `@ablo/sync-engine`.
3
+ *
4
+ * Inlined directly so the publishable dist is self-contained. The public
5
+ * package should never reference an unpublished internal package from emitted
6
+ * JS; strict bundlers surface that immediately.
7
+ *
8
+ * ### Two patterns for consumers
9
+ *
10
+ * ```ts
11
+ * // Stripe-style instanceof
12
+ * if (err instanceof AbloRateLimitError) backoff(err.retryAfterSeconds);
13
+ *
14
+ * // Discriminator-string (for cross-boundary cases where the class
15
+ * // identity gets lost, e.g. across a web worker postMessage)
16
+ * if (err.type === 'AbloRateLimitError') { ... }
17
+ * ```
18
+ *
19
+ * Both work on every subclass.
20
+ */
21
+ // ── AbloError hierarchy — the typed error surface ────────────────────
22
+ /** Common shape for all errors thrown by this SDK. */
23
+ export class AbloError extends Error {
24
+ /** Discriminator string — matches the class name. Lets consumers
25
+ * switch on `e.type` without `instanceof` checks across package
26
+ * boundaries (matches Stripe's `err.type` pattern). */
27
+ type = 'AbloError';
28
+ /** Stable short identifier for logs + metrics.
29
+ * E.g. `'apikey_invalid'`, `'capability_scope_denied'`. */
30
+ code;
31
+ /** HTTP status code when the error originated from an HTTP response. */
32
+ httpStatus;
33
+ /** Correlation id for ops — present when the server sent one on
34
+ * `x-request-id`. Include in support tickets. */
35
+ requestId;
36
+ constructor(message, options) {
37
+ super(message);
38
+ this.name = this.constructor.name;
39
+ if (options?.code !== undefined)
40
+ this.code = options.code;
41
+ if (options?.httpStatus !== undefined)
42
+ this.httpStatus = options.httpStatus;
43
+ if (options?.requestId !== undefined)
44
+ this.requestId = options.requestId;
45
+ if (options?.cause !== undefined) {
46
+ Object.defineProperty(this, 'cause', { value: options.cause, enumerable: false });
47
+ }
48
+ }
49
+ }
50
+ /** 401 — invalid/missing/expired credentials. */
51
+ export class AbloAuthenticationError extends AbloError {
52
+ type = 'AbloAuthenticationError';
53
+ }
54
+ /** 403 — credentials were valid but the action is forbidden (scope
55
+ * denial, revoked capability, role not authorized). */
56
+ export class AbloPermissionError extends AbloError {
57
+ type = 'AbloPermissionError';
58
+ }
59
+ /** 429 — rate limit exceeded. Consumers should back off before retry. */
60
+ export class AbloRateLimitError extends AbloError {
61
+ type = 'AbloRateLimitError';
62
+ retryAfterSeconds;
63
+ constructor(message, options) {
64
+ super(message, options);
65
+ if (options?.retryAfterSeconds !== undefined) {
66
+ this.retryAfterSeconds = options.retryAfterSeconds;
67
+ }
68
+ }
69
+ }
70
+ /** 409 — same `Idempotency-Key` reused with a different request body. */
71
+ export class AbloIdempotencyError extends AbloError {
72
+ type = 'AbloIdempotencyError';
73
+ }
74
+ /** Network / transport failure — TCP reset, DNS, timeout, abort. */
75
+ export class AbloConnectionError extends AbloError {
76
+ type = 'AbloConnectionError';
77
+ }
78
+ /** 400 / 422 — request payload was invalid. */
79
+ export class AbloValidationError extends AbloError {
80
+ type = 'AbloValidationError';
81
+ }
82
+ /** 5xx — server-side error. Usually retryable with backoff. */
83
+ export class AbloServerError extends AbloError {
84
+ type = 'AbloServerError';
85
+ }
86
+ /**
87
+ * 409 — a write carried `readAt: N` but the target entity has received
88
+ * deltas since `N`. The caller's reasoning snapshot is stale; the safe
89
+ * response is to re-read (or re-capture a watermark) and regenerate.
90
+ *
91
+ * Carries `conflicts` so callers can inspect which specific (model, id)
92
+ * pairs moved during the generation window — useful for metrics
93
+ * ("72% of stale rejects were on slide titles") and for selective
94
+ * regeneration (only re-think the slides that changed, not the whole
95
+ * deck).
96
+ */
97
+ export class AbloStaleContextError extends AbloError {
98
+ type = 'AbloStaleContextError';
99
+ /** Sync id at the caller's `readAt` when the write was attempted. */
100
+ readAt;
101
+ /** Entities that received deltas between `readAt` and the write. */
102
+ conflicts;
103
+ constructor(message, options) {
104
+ super(message, options);
105
+ if (options?.readAt !== undefined)
106
+ this.readAt = options.readAt;
107
+ if (options?.conflicts !== undefined)
108
+ this.conflicts = options.conflicts;
109
+ }
110
+ }
111
+ /**
112
+ * The target entity currently has an active intent held by another
113
+ * participant and the caller asked the SDK not to return immediately.
114
+ *
115
+ * Use `ifBusy: 'wait'` to wait for the intent stream to clear, or
116
+ * `ifBusy: 'return'` to inspect the active intents yourself.
117
+ */
118
+ export class AbloBusyError extends AbloError {
119
+ type = 'AbloBusyError';
120
+ intents;
121
+ constructor(message, options) {
122
+ super(message, options);
123
+ if (options?.intents !== undefined)
124
+ this.intents = options.intents;
125
+ }
126
+ }
127
+ /**
128
+ * Biscuit capability token failed verification — either it's
129
+ * malformed / unknown / revoked (`capability_invalid`), or its
130
+ * caveats deny the attempted action (`capability_scope_denied`).
131
+ *
132
+ * Extends `AbloPermissionError` so existing `instanceof CapabilityError`
133
+ * checks keep working AND broader `instanceof AbloPermissionError`
134
+ * matches for consumers who don't care about the Biscuit specifics.
135
+ *
136
+ * `requiredCapability` (when present) describes what an attenuated
137
+ * capability must satisfy for the request to succeed on retry.
138
+ */
139
+ export class CapabilityError extends AbloPermissionError {
140
+ requiredCapability;
141
+ constructor(code, message, requiredCapability) {
142
+ super(`${code}: ${message}`, { code });
143
+ this.name = 'CapabilityError';
144
+ if (requiredCapability !== undefined) {
145
+ this.requiredCapability = requiredCapability;
146
+ }
147
+ }
148
+ }
149
+ // ── Legacy session error (now part of the typed hierarchy) ───────────
150
+ /**
151
+ * SyncSessionError — Thrown when authentication/session is invalid or expired.
152
+ * Signals that the user should be redirected to sign in
153
+ * rather than showing a generic retry option.
154
+ *
155
+ * Extends `AbloAuthenticationError` so existing
156
+ * `SyncSessionError.isSessionError(...)` duck-type callers keep
157
+ * working, AND downstream code that only catches the typed hierarchy
158
+ * (`instanceof AbloAuthenticationError` / `e.type === 'AbloAuthenticationError'`)
159
+ * now sees session failures too.
160
+ */
161
+ export class SyncSessionError extends AbloAuthenticationError {
162
+ isSessionError = true;
163
+ statusCode;
164
+ constructor(message, statusCode = 401) {
165
+ super(message, { httpStatus: statusCode, code: 'session_expired' });
166
+ this.name = 'SyncSessionError';
167
+ this.statusCode = statusCode;
168
+ if (Error.captureStackTrace) {
169
+ Error.captureStackTrace(this, SyncSessionError);
170
+ }
171
+ }
172
+ /**
173
+ * Check if an error is a session error (duck-type check)
174
+ */
175
+ static isSessionError(error) {
176
+ if (error instanceof SyncSessionError) {
177
+ return true;
178
+ }
179
+ if (error && typeof error === 'object' && 'isSessionError' in error) {
180
+ return error.isSessionError === true;
181
+ }
182
+ return false;
183
+ }
184
+ /**
185
+ * Check if an HTTP response status indicates a session error
186
+ */
187
+ static isSessionErrorResponse(status, body) {
188
+ if (status === 401)
189
+ return true;
190
+ if (status === 403) {
191
+ if (body) {
192
+ const lowerBody = body.toLowerCase();
193
+ if (lowerBody.includes('session') ||
194
+ lowerBody.includes('unauthorized') ||
195
+ lowerBody.includes('not authenticated') ||
196
+ lowerBody.includes('token')) {
197
+ return true;
198
+ }
199
+ }
200
+ return true;
201
+ }
202
+ return false;
203
+ }
204
+ }
205
+ /**
206
+ * Translate an HTTP response into the appropriate typed error.
207
+ *
208
+ * Single source of truth for status-code → class mapping — every SDK
209
+ * fetch path that sees a non-2xx response should route through here
210
+ * so the customer-visible error is always the right subclass.
211
+ */
212
+ export function translateHttpError(status, body, requestId) {
213
+ const parsed = typeof body === 'object' && body !== null ? body : {};
214
+ const nested = parsed.error != null && typeof parsed.error === 'object'
215
+ ? parsed.error
216
+ : undefined;
217
+ const flatError = typeof parsed.error === 'string' ? parsed.error : undefined;
218
+ const code = parsed.code ?? nested?.code ?? flatError;
219
+ const message = nested?.message ??
220
+ parsed.reason ??
221
+ parsed.message ??
222
+ flatError ??
223
+ (typeof body === 'string' ? body : `HTTP ${status}`);
224
+ const requiredCapability = nested?.requiredCapability ?? parsed.requiredCapability;
225
+ const baseOpts = { code, httpStatus: status, requestId };
226
+ if (status === 401)
227
+ return new AbloAuthenticationError(message, baseOpts);
228
+ if (status === 403 || code === 'capability_scope_denied' || code === 'capability_invalid') {
229
+ if (code === 'capability_scope_denied' || code === 'capability_invalid') {
230
+ return new CapabilityError(code, message, requiredCapability);
231
+ }
232
+ return new AbloPermissionError(message, baseOpts);
233
+ }
234
+ if (status === 409)
235
+ return new AbloIdempotencyError(message, baseOpts);
236
+ if (status === 422 || status === 400)
237
+ return new AbloValidationError(message, baseOpts);
238
+ if (status === 429)
239
+ return new AbloRateLimitError(message, baseOpts);
240
+ if (status >= 500)
241
+ return new AbloServerError(message, baseOpts);
242
+ return new AbloError(message, baseOpts);
243
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @ablo/sync-engine — The Collaboration Layer for AI and Humans
3
+ *
4
+ * ```ts
5
+ * import Ablo from '@ablo/sync-engine';
6
+ *
7
+ * const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
8
+ * await ablo.tasks.load({ where: { id: 'task_123' } });
9
+ * await ablo.tasks.update('task_123', { title: 'Fix bug' });
10
+ *
11
+ * type Entry = Ablo.Peer;
12
+ * ```
13
+ *
14
+ * `Ablo({ schema, apiKey })` gives typed model resources. `Ablo({ apiKey })`
15
+ * gives the lower-level Resource / Intent / Commit client for agents,
16
+ * MCP routes, and custom runtimes.
17
+ *
18
+ * Stripe / Anthropic / OpenAI all do this: one import, resources
19
+ * reached via dot-access on the engine, types via namespace dots.
20
+ *
21
+ * Public subpaths:
22
+ * @ablo/sync-engine/schema — defineSchema, model, z (Zod)
23
+ * @ablo/sync-engine/react — <AbloProvider>, useQuery, useMutate
24
+ * @ablo/sync-engine/testing — test harnesses + mocks
25
+ *
26
+ * Consumer code should converge on `ablo.<model>.load(...)`, which routes
27
+ * through the engine's `HydrationCoordinator` and dedupes single-flight
28
+ * hydrations.
29
+ */
30
+ export { Ablo } from './client/Ablo.js';
31
+ export type { AbloOptions, ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelEditHandle, ModelEditOptions, ModelOperations, } from './client/Ablo.js';
32
+ export type { AbloPersistence } from './client/persistence.js';
33
+ export { session, agent } from './principal.js';
34
+ import { Ablo } from './client/Ablo.js';
35
+ export default Ablo;
36
+ export { dataSource, abloSource, signAbloSourceRequest, verifyAbloSourceRequest, } from './source/index.js';
37
+ export { defaultPolicy } from './policy/index.js';
38
+ export { SyncSessionError, AbloError, AbloAuthenticationError, AbloPermissionError, AbloRateLimitError, AbloIdempotencyError, AbloConnectionError, AbloValidationError, AbloServerError, AbloStaleContextError, AbloBusyError, CapabilityError, translateHttpError, } from './errors.js';
39
+ export type { CommitReceipt, RequiredCapability } from './errors.js';
40
+ export { defineMutators } from './mutators/defineMutators.js';
41
+ export { createTransaction, type Transaction } from './mutators/Transaction.js';
package/dist/index.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @ablo/sync-engine — The Collaboration Layer for AI and Humans
3
+ *
4
+ * ```ts
5
+ * import Ablo from '@ablo/sync-engine';
6
+ *
7
+ * const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
8
+ * await ablo.tasks.load({ where: { id: 'task_123' } });
9
+ * await ablo.tasks.update('task_123', { title: 'Fix bug' });
10
+ *
11
+ * type Entry = Ablo.Peer;
12
+ * ```
13
+ *
14
+ * `Ablo({ schema, apiKey })` gives typed model resources. `Ablo({ apiKey })`
15
+ * gives the lower-level Resource / Intent / Commit client for agents,
16
+ * MCP routes, and custom runtimes.
17
+ *
18
+ * Stripe / Anthropic / OpenAI all do this: one import, resources
19
+ * reached via dot-access on the engine, types via namespace dots.
20
+ *
21
+ * Public subpaths:
22
+ * @ablo/sync-engine/schema — defineSchema, model, z (Zod)
23
+ * @ablo/sync-engine/react — <AbloProvider>, useQuery, useMutate
24
+ * @ablo/sync-engine/testing — test harnesses + mocks
25
+ *
26
+ * Consumer code should converge on `ablo.<model>.load(...)`, which routes
27
+ * through the engine's `HydrationCoordinator` and dedupes single-flight
28
+ * hydrations.
29
+ */
30
+ // ── Consumer API ──────────────────────────────────────────────────────────
31
+ // These are the only symbols external consumers should need from this path.
32
+ // Everything else is in a subpath.
33
+ // The canonical surface — `Ablo` is a function, type, and namespace under
34
+ // one name. Matches `Stripe`, `OpenAI`, `Anthropic`. Default export so
35
+ // `import Ablo from '@ablo/sync-engine'` works; named export so
36
+ // `import { Ablo }` also compiles.
37
+ export { Ablo } from './client/Ablo.js';
38
+ // Participant types live under `Ablo.Participant.*` —
39
+ // `Ablo.Participant.Joined`, `Ablo.Participant.Manager`,
40
+ // `Ablo.Participant.JoinOptions`, etc. Same dot-access shape as
41
+ // `Ablo.Peer`, `Ablo.Claim`, `Ablo.Turn`. No flat re-exports.
42
+ // Principal constructors — explicit factories for delegation paths
43
+ // (`Ablo({ kind: 'agent', as: session({...}) })`). Function exports
44
+ // because they're constructors, not types.
45
+ export { session, agent } from './principal.js';
46
+ import { Ablo } from './client/Ablo.js';
47
+ export default Ablo;
48
+ // Customer-owned storage adapter. Used only when Ablo Cloud coordinates
49
+ // state while canonical rows remain in the customer's database. Runtime
50
+ // helpers ship flat; type counterparts live under `Ablo.Source.*`
51
+ // (`Ablo.Source.Operation`, `Ablo.Source.Commit.Params`, etc.).
52
+ export { dataSource, abloSource, signAbloSourceRequest, verifyAbloSourceRequest, } from './source/index.js';
53
+ // Schema DSL is intentionally published from `@ablo/sync-engine/schema`.
54
+ // Keeping it out of the root import preserves one clean runtime surface:
55
+ // `import Ablo from '@ablo/sync-engine'`.
56
+ // Conflict policy — `defaultPolicy` (the rejecting default) is a value
57
+ // callers reference if they want to compose. The type counterparts
58
+ // (`Conflict`, `ConflictPolicy`, etc.) live under `Ablo.Conflict`,
59
+ // `Ablo.Conflict.Policy` on the namespace.
60
+ export { defaultPolicy } from './policy/index.js';
61
+ // Typed error hierarchy — Stripe-style. One import gets every class
62
+ // consumers need to discriminate failures (`e instanceof AbloX` or
63
+ // `e.type === 'AbloX'`) plus the HTTP-response translator.
64
+ export { SyncSessionError, AbloError, AbloAuthenticationError, AbloPermissionError, AbloRateLimitError, AbloIdempotencyError, AbloConnectionError, AbloValidationError, AbloServerError, AbloStaleContextError, AbloBusyError, CapabilityError, translateHttpError, } from './errors.js';
65
+ // Typed-global augmentation point. Consumers declare their Schema/Presence/
66
+ // Intents/UserMeta once in a `.d.ts` via `declare global { interface AbloSync
67
+ // { ... } }`. Resolver types live under the `Ablo` namespace —
68
+ // `Ablo.ResolveSchema`, `Ablo.ResolvePresence`, etc. — pure type-level.
69
+ // Custom mutators — runtime entry point only.
70
+ // Type counterparts moved to the `Ablo` namespace:
71
+ // Ablo.Mutator.Fn, Ablo.Transaction
72
+ // Ablo.Mutator.UndoEntry, Ablo.Mutator.InverseOp
73
+ // Ablo.Query, Ablo.QueryBatch, Ablo.QueryBatchResult
74
+ export { defineMutators } from './mutators/defineMutators.js';
75
+ // `createTransaction` is exposed so non-React callers (the sandbox AI
76
+ // executor, server-side workers) can invoke `defineMutators`-style
77
+ // custom mutators without going through `useMutators`. Construct a tx
78
+ // against `ablo.schema` + `ablo._store` + `ablo.organizationId` and
79
+ // pass it as `{ tx, args }` to the mutator function.
80
+ export { createTransaction } from './mutators/Transaction.js';
81
+ // Undo runtime is intentionally not part of the public root surface. App code
82
+ // uses `useUndoScope` from `@ablo/sync-engine/react`.