@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,63 @@
1
+ /**
2
+ * Participant identity + scope resolution for `Ablo()`.
3
+ *
4
+ * Three branches, mirroring the three auth paths the SDK supports:
5
+ *
6
+ * 1. **Hosted-cloud** — caller passed `apiKey`. SDK exchanges it
7
+ * server-side for a capability token + scope blob, then sets
8
+ * up a refresh scheduler that re-mints transparently before
9
+ * expiry.
10
+ * 2. **Self-derived** — caller passed an authToken / capability
11
+ * token but the SDK doesn't yet know the identity. Calls
12
+ * `resolveIdentity` against the bootstrap endpoint to recover
13
+ * `participantId` + scope from the token.
14
+ * 3. **Legacy explicit** — self-hosted callers that pass
15
+ * `organizationId` + `user.id` (or `agentId`) directly. No
16
+ * server round-trip; SDK trusts the caller.
17
+ *
18
+ * Extracted from `Ablo.ts` so each branch is testable in isolation
19
+ * and the constructor body reads as a single named call rather than
20
+ * a 100+-line if/elif/else with three different side-effect chains.
21
+ */
22
+ import { type RefreshScheduler } from '../auth/index.js';
23
+ import type { BootstrapHelper } from '../sync/BootstrapHelper.js';
24
+ import type { SyncLogger } from '../interfaces/index.js';
25
+ import type { ApiKeySetter } from './auth.js';
26
+ export interface IdentityResolveInput {
27
+ readonly options: {
28
+ readonly capabilityToken?: string;
29
+ readonly bootstrapBaseUrl?: string;
30
+ readonly user?: {
31
+ id: string;
32
+ teamIds?: string[];
33
+ };
34
+ readonly agentId?: string;
35
+ readonly syncGroups?: string[];
36
+ };
37
+ readonly internalOptions: {
38
+ readonly organizationId?: string;
39
+ };
40
+ readonly url: string;
41
+ readonly kind: 'user' | 'agent' | 'system';
42
+ readonly configuredApiKey: string | ApiKeySetter | null;
43
+ readonly configuredAuthToken: string | null;
44
+ readonly bootstrapHelper: BootstrapHelper;
45
+ readonly logger: SyncLogger;
46
+ /**
47
+ * Called when the hosted-cloud refresh scheduler rotates the
48
+ * capability token — caller forwards the new token to the live
49
+ * WebSocket via `ws.setCapabilityToken(...)`.
50
+ */
51
+ readonly applyRotatedToken: (token: string) => void;
52
+ }
53
+ export interface ResolvedIdentity {
54
+ readonly userId: string;
55
+ readonly accountScope: string;
56
+ readonly teamIds: string[] | undefined;
57
+ readonly capabilityToken: string | undefined;
58
+ readonly syncGroups: readonly string[] | undefined;
59
+ readonly participantKind: 'user' | 'agent' | 'system';
60
+ /** Non-null on the hosted-cloud path; caller stores it for shutdown. */
61
+ readonly refreshScheduler: RefreshScheduler | null;
62
+ }
63
+ export declare function resolveParticipantIdentity(input: IdentityResolveInput): Promise<ResolvedIdentity>;
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Participant identity + scope resolution for `Ablo()`.
3
+ *
4
+ * Three branches, mirroring the three auth paths the SDK supports:
5
+ *
6
+ * 1. **Hosted-cloud** — caller passed `apiKey`. SDK exchanges it
7
+ * server-side for a capability token + scope blob, then sets
8
+ * up a refresh scheduler that re-mints transparently before
9
+ * expiry.
10
+ * 2. **Self-derived** — caller passed an authToken / capability
11
+ * token but the SDK doesn't yet know the identity. Calls
12
+ * `resolveIdentity` against the bootstrap endpoint to recover
13
+ * `participantId` + scope from the token.
14
+ * 3. **Legacy explicit** — self-hosted callers that pass
15
+ * `organizationId` + `user.id` (or `agentId`) directly. No
16
+ * server round-trip; SDK trusts the caller.
17
+ *
18
+ * Extracted from `Ablo.ts` so each branch is testable in isolation
19
+ * and the constructor body reads as a single named call rather than
20
+ * a 100+-line if/elif/else with three different side-effect chains.
21
+ */
22
+ import { AbloAuthenticationError } from '../errors.js';
23
+ import { exchangeApiKey } from '../auth/index.js';
24
+ import { resolveIdentity } from '../auth/index.js';
25
+ import { createRefreshScheduler, } from '../auth/index.js';
26
+ import { resolveApiKeyValue } from './auth.js';
27
+ export async function resolveParticipantIdentity(input) {
28
+ const { options, internalOptions, url, kind, configuredApiKey, configuredAuthToken, bootstrapHelper, logger, applyRotatedToken, } = input;
29
+ const apiKeyValue = await resolveApiKeyValue(configuredApiKey);
30
+ const initialCapToken = options.capabilityToken ?? configuredAuthToken ?? undefined;
31
+ // Branch 1: hosted-cloud (apiKey only, no caller-supplied capability token)
32
+ if (apiKeyValue && !options.capabilityToken) {
33
+ return resolveHosted({
34
+ apiKeyValue,
35
+ configuredApiKey,
36
+ url,
37
+ kind,
38
+ options,
39
+ bootstrapHelper,
40
+ logger,
41
+ applyRotatedToken,
42
+ });
43
+ }
44
+ // Branch 2: self-derived (capability token present, identity unknown)
45
+ if (!internalOptions.organizationId ||
46
+ (kind === 'agent' ? !options.agentId : !options.user?.id)) {
47
+ const baseUrl = options.bootstrapBaseUrl ?? `${url.replace(/^ws/, 'http')}/api`;
48
+ const identity = await resolveIdentity({
49
+ baseUrl,
50
+ authToken: initialCapToken,
51
+ });
52
+ // Merge caller-passed syncGroups with server-resolved ones rather
53
+ // than letting the server's response silently overwrite. Browser
54
+ // consumers (apps/web's SyncEngineProvider) compose
55
+ // `['default', 'org:${orgId}', 'user:${userId}', ...team:]` from
56
+ // the resolved session and pass it via `<AbloProvider syncGroups>`;
57
+ // before this merge, Branch 2 dropped that set on the floor in
58
+ // favor of `/auth/identity`'s response, which is empty for
59
+ // cookie-auth users today (apps/sync-server/src/routes/auth.ts only
60
+ // populates from `effectiveSyncGroups`, the cap-narrowed list).
61
+ // Empty syncGroups → server bootstrap falls back to `['default']`
62
+ // → no deltas fan out → live updates appear only on hard reload.
63
+ const callerGroups = options.syncGroups ?? [];
64
+ const mergedSyncGroups = callerGroups.length > 0
65
+ ? [...new Set([...callerGroups, ...identity.syncGroups])]
66
+ : identity.syncGroups;
67
+ bootstrapHelper.setCacheScope(identity.accountScope);
68
+ bootstrapHelper.setSyncGroups(mergedSyncGroups);
69
+ bootstrapHelper.setAuthToken(initialCapToken);
70
+ return {
71
+ userId: identity.participantId,
72
+ accountScope: identity.accountScope,
73
+ teamIds: undefined,
74
+ capabilityToken: initialCapToken,
75
+ syncGroups: mergedSyncGroups,
76
+ participantKind: identity.participantKind,
77
+ refreshScheduler: null,
78
+ };
79
+ }
80
+ // Branch 3: legacy explicit (self-hosted, pre-Phase-3 — caller knows
81
+ // its own organizationId + user/agentId).
82
+ const userId = kind === 'agent' ? options.agentId : options.user.id;
83
+ const accountScope = internalOptions.organizationId;
84
+ bootstrapHelper.setCacheScope(accountScope);
85
+ bootstrapHelper.setSyncGroups(options.syncGroups);
86
+ bootstrapHelper.setAuthToken(initialCapToken);
87
+ return {
88
+ userId,
89
+ accountScope,
90
+ teamIds: kind === 'user' ? options.user?.teamIds : undefined,
91
+ capabilityToken: initialCapToken,
92
+ syncGroups: options.syncGroups,
93
+ participantKind: kind,
94
+ refreshScheduler: null,
95
+ };
96
+ }
97
+ async function resolveHosted(input) {
98
+ // Pure managed-cloud shape: `Ablo({schema, apiKey})`. Server returns
99
+ // scope + userMeta; SDK populates internals.
100
+ const baseUrl = input.options.bootstrapBaseUrl ??
101
+ `${input.url.replace(/^ws/, 'http')}/api`;
102
+ const exchangeArgs = {
103
+ baseUrl,
104
+ participantKind: (input.kind === 'agent' ? 'agent' : 'system'),
105
+ participantId: input.options.agentId ?? input.options.user?.id,
106
+ wideScope: true,
107
+ ttlSeconds: 3600,
108
+ };
109
+ const exchange = await exchangeApiKey({
110
+ ...exchangeArgs,
111
+ apiKey: input.apiKeyValue,
112
+ });
113
+ input.bootstrapHelper.setCacheScope(exchange.scope.organizationId);
114
+ input.bootstrapHelper.setSyncGroups(exchange.scope.syncGroups);
115
+ input.bootstrapHelper.setAuthToken(exchange.token);
116
+ // Cap tokens have a server-set TTL (3600s by default). Without
117
+ // proactive refresh the WS would either get force-closed at expiry
118
+ // or fail its next reconnect with 401. The scheduler re-mints
119
+ // transparently before that fires; the consumer never sees the
120
+ // rotation. Rationale + tradeoffs in
121
+ // `packages/sync-engine/src/auth/refreshScheduler.ts`
122
+ const refreshScheduler = createRefreshScheduler({
123
+ initialExpiresAtMs: Date.parse(exchange.expiresAt),
124
+ refresh: async () => {
125
+ // Read the apiKey fresh each time — supports the ApiKeySetter
126
+ // (rotating credentials) shape.
127
+ const freshApiKey = await resolveApiKeyValue(input.configuredApiKey);
128
+ if (!freshApiKey) {
129
+ throw new AbloAuthenticationError('apiKey unavailable during refresh', { code: 'apikey_missing' });
130
+ }
131
+ const next = await exchangeApiKey({
132
+ ...exchangeArgs,
133
+ apiKey: freshApiKey,
134
+ });
135
+ input.bootstrapHelper.setAuthToken(next.token);
136
+ input.applyRotatedToken(next.token);
137
+ return { expiresAtMs: Date.parse(next.expiresAt) };
138
+ },
139
+ onError: (err) => {
140
+ input.logger.warn('cap token refresh failed; will retry', {
141
+ error: err.message,
142
+ });
143
+ },
144
+ });
145
+ return {
146
+ userId: exchange.scope.participantId,
147
+ accountScope: exchange.scope.organizationId,
148
+ // teamIds isn't needed because the server already encoded
149
+ // team-level access into scope.syncGroups.
150
+ teamIds: undefined,
151
+ capabilityToken: exchange.token,
152
+ syncGroups: exchange.scope.syncGroups,
153
+ participantKind: input.kind,
154
+ refreshScheduler,
155
+ };
156
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @ablo/sync-engine/client — Consumer API
3
+ *
4
+ * The one-liner entry point for external consumers.
5
+ *
6
+ * `Ablo({ apiKey })` is the stateless HTTP API client. Add `schema`
7
+ * when you want the realtime sync engine with typed model proxies.
8
+ *
9
+ * ```ts
10
+ * import { Ablo } from '@ablo/sync-engine/client';
11
+ * import { schema } from './schema';
12
+ *
13
+ * const ablo = Ablo({
14
+ * schema,
15
+ * apiKey: process.env.ABLO_API_KEY,
16
+ * });
17
+ *
18
+ * const tasks = ablo.tasks.list({ where: { status: 'todo' } });
19
+ * await ablo.tasks.create({ title: 'New task' });
20
+ * ```
21
+ *
22
+ * For headless agents (workers, bots), pass `kind: 'agent'` plus a
23
+ * Biscuit `capabilityToken`:
24
+ *
25
+ * ```ts
26
+ * const bot = Ablo({
27
+ * schema,
28
+ * apiKey: process.env.ABLO_API_KEY,
29
+ * kind: 'agent',
30
+ * });
31
+ * ```
32
+ */
33
+ export { Ablo, computeFKDepthPriority, type AbloOptions, type InternalAbloOptions, type BusyOptions, type BusyPolicy, type IntentWaitOptions, type ModelCountOptions, type ModelListOptions, type ModelListScope, type ModelLoadOptions, type ModelOperations, type ResourceReadOptions, } from './Ablo.js';
34
+ export type { AbloPersistence } from './persistence.js';
35
+ export type { AbloApi, AbloApiClientOptions, AbloApiIntents, Agent, AgentIntentInput, AgentIntentOptions, AgentOptions, AgentResourceClient, AgentResourceReadOptions, AgentResourceMutationOptions, AgentRunContext, AgentRunDone, AgentRunFailed, AgentRunCancelled, AgentRunOptions, AgentRunResult, AgentRunStatus, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, Task, TaskCloseOptions, TaskCloseResult, TaskCreateOptions, TaskResource, } from './ApiClient.js';
36
+ export type { EngineParticipant, JoinedParticipant, ParticipantJoinOptions, ParticipantManager, ParticipantScope, ParticipantStatus, ScopedIntents, ScopedPresence, } from '../sync/participants.js';
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @ablo/sync-engine/client — Consumer API
3
+ *
4
+ * The one-liner entry point for external consumers.
5
+ *
6
+ * `Ablo({ apiKey })` is the stateless HTTP API client. Add `schema`
7
+ * when you want the realtime sync engine with typed model proxies.
8
+ *
9
+ * ```ts
10
+ * import { Ablo } from '@ablo/sync-engine/client';
11
+ * import { schema } from './schema';
12
+ *
13
+ * const ablo = Ablo({
14
+ * schema,
15
+ * apiKey: process.env.ABLO_API_KEY,
16
+ * });
17
+ *
18
+ * const tasks = ablo.tasks.list({ where: { status: 'todo' } });
19
+ * await ablo.tasks.create({ title: 'New task' });
20
+ * ```
21
+ *
22
+ * For headless agents (workers, bots), pass `kind: 'agent'` plus a
23
+ * Biscuit `capabilityToken`:
24
+ *
25
+ * ```ts
26
+ * const bot = Ablo({
27
+ * schema,
28
+ * apiKey: process.env.ABLO_API_KEY,
29
+ * kind: 'agent',
30
+ * });
31
+ * ```
32
+ */
33
+ export { Ablo, computeFKDepthPriority, } from './Ablo.js';
@@ -0,0 +1,7 @@
1
+ export type AbloPersistence = 'volatile' | 'indexeddb';
2
+ export interface PersistenceOptions {
3
+ readonly persistence?: AbloPersistence | undefined;
4
+ readonly inMemory?: boolean | undefined;
5
+ readonly offline?: boolean | undefined;
6
+ }
7
+ export declare function shouldUseInMemoryPersistence(options: PersistenceOptions): boolean;
@@ -0,0 +1,11 @@
1
+ export function shouldUseInMemoryPersistence(options) {
2
+ if (typeof window === 'undefined')
3
+ return true;
4
+ if (options.persistence)
5
+ return options.persistence === 'volatile';
6
+ if (typeof options.inMemory === 'boolean')
7
+ return options.inMemory;
8
+ if (options.offline === true)
9
+ return false;
10
+ return true;
11
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Up-front validation of `AbloOptions`. Returns the first error
3
+ * encountered or null if all checks pass — caller writes the error
4
+ * into `store.syncStatus` so consumers see it through the existing
5
+ * status surface rather than catching it at the call site.
6
+ *
7
+ * Extracted from `Ablo.ts` (which was ~2300 LOC of constructor wiring)
8
+ * so the validation rules are readable in isolation. The order of
9
+ * checks matters: missing `url` is checked before identity options
10
+ * because the error messages reference URLs and would mislead if a
11
+ * URL was actually present.
12
+ */
13
+ /**
14
+ * Minimal subset of `AbloOptions` the validator actually inspects.
15
+ * Defined here as its own interface so the validator doesn't pull
16
+ * the whole 200+-line `AbloOptions` type — and to avoid a circular
17
+ * import with `Ablo.ts`. Fields are kept structurally identical to
18
+ * `AbloOptions` so a real options object satisfies this shape.
19
+ */
20
+ export interface ValidatableAbloOptions {
21
+ readonly schema?: {
22
+ readonly models?: Record<string, unknown>;
23
+ } | null;
24
+ readonly kind?: 'user' | 'agent' | 'system';
25
+ readonly user?: {
26
+ readonly id?: string;
27
+ } | undefined;
28
+ readonly agentId?: string | undefined;
29
+ readonly capabilityToken?: string | undefined;
30
+ }
31
+ export interface ValidateAbloOptionsInput {
32
+ readonly options: ValidatableAbloOptions;
33
+ readonly url: string;
34
+ /**
35
+ * Truthy when an API key was supplied (string or callable). The
36
+ * validator only inspects presence, never the value, so the input
37
+ * shape stays loose to accept whatever the caller resolved.
38
+ */
39
+ readonly configuredApiKey: unknown;
40
+ readonly configuredAuthToken: unknown;
41
+ }
42
+ export declare function validateAbloOptions(input: ValidateAbloOptionsInput): Error | null;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Up-front validation of `AbloOptions`. Returns the first error
3
+ * encountered or null if all checks pass — caller writes the error
4
+ * into `store.syncStatus` so consumers see it through the existing
5
+ * status surface rather than catching it at the call site.
6
+ *
7
+ * Extracted from `Ablo.ts` (which was ~2300 LOC of constructor wiring)
8
+ * so the validation rules are readable in isolation. The order of
9
+ * checks matters: missing `url` is checked before identity options
10
+ * because the error messages reference URLs and would mislead if a
11
+ * URL was actually present.
12
+ */
13
+ export function validateAbloOptions(input) {
14
+ const { options, url, configuredApiKey, configuredAuthToken } = input;
15
+ const kind = options.kind ?? 'user';
16
+ if (!url) {
17
+ return new Error('Ablo: `url` is required. Pass the sync server URL, e.g. ' +
18
+ `Ablo({ baseURL: 'wss://sync.ablo.dev', schema, user })`);
19
+ }
20
+ // Schema is optional for the resource-first API:
21
+ // Ablo({ apiKey }).resource('clauses').retrieve(...)
22
+ // Passing a schema only enables typed model sugar (`ablo.tasks.update(...)`).
23
+ if (!configuredApiKey &&
24
+ !configuredAuthToken &&
25
+ !options.capabilityToken &&
26
+ kind === 'user' &&
27
+ options.user &&
28
+ !options.user.id) {
29
+ return new Error('Ablo: `user.id` must be a non-empty string when `user` is provided.');
30
+ }
31
+ if (!configuredApiKey && !configuredAuthToken && kind === 'agent' && !options.agentId) {
32
+ return new Error('Ablo: provide either `apiKey` or `agentId` for `kind: "agent"`. ' +
33
+ 'Hosted-cloud consumers pass `apiKey` and the server derives the ' +
34
+ 'agent identity from its scope; self-hosted passes `agentId` + ' +
35
+ '`capabilityToken` directly.');
36
+ }
37
+ if (!configuredApiKey && !configuredAuthToken && kind === 'agent' && !options.capabilityToken) {
38
+ return new Error('Ablo: provide either `apiKey` (hosted cloud — SDK exchanges internally) ' +
39
+ 'or `capabilityToken` (self-hosted — your auth layer mints + hands in). ' +
40
+ 'See https://ablo.dev/docs/api-keys for the full pattern.');
41
+ }
42
+ return null;
43
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @ablo/sync-engine/config — App initialization
3
+ *
4
+ * One-time setup at app boot. Provides DI interface types
5
+ * and the initSyncEngine() function to wire real implementations.
6
+ */
7
+ export { initSyncEngine, resetSyncEngine, isSyncEngineInitialized } from '../context.js';
8
+ export { noopLogger, noopObservability, noopAnalytics, browserOnlineStatus, defaultSessionErrorDetector, emptyConfig, type SyncEngineContext, } from '../SyncEngineContext.js';
9
+ export type { SyncEngineConfig, SyncLogger, SyncObservabilityProvider, SyncAnalytics, MutationExecutor, MutationDispatcher, SessionErrorDetector, OnlineStatusProvider, CommitResult, MutationOperation, BreadcrumbLevel, SyncBreadcrumbCategory, TransactionFailureDetails, BootstrapFailureDetails, WebSocketErrorDetails, RollbackDetails, SpanAttributes, } from '../interfaces/index.js';
10
+ export { SyncSessionError } from '../errors.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @ablo/sync-engine/config — App initialization
3
+ *
4
+ * One-time setup at app boot. Provides DI interface types
5
+ * and the initSyncEngine() function to wire real implementations.
6
+ */
7
+ // Context lifecycle
8
+ export { initSyncEngine, resetSyncEngine, isSyncEngineInitialized } from '../context.js';
9
+ // Context type + no-op defaults (for testing or gradual adoption)
10
+ export { noopLogger, noopObservability, noopAnalytics, browserOnlineStatus, defaultSessionErrorDetector, emptyConfig, } from '../SyncEngineContext.js';
11
+ // Errors
12
+ export { SyncSessionError } from '../errors.js';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Module-level context accessor
3
+ *
4
+ * Set once during SDK initialization via `initSyncEngine(context)`.
5
+ * All internal SDK files access dependencies through `getContext()`.
6
+ * This avoids threading context through every constructor.
7
+ */
8
+ import type { SyncEngineContext } from './SyncEngineContext.js';
9
+ /**
10
+ * Initialize the sync engine with application-provided dependencies.
11
+ * Must be called before any sync engine operations.
12
+ */
13
+ export declare function initSyncEngine(context: SyncEngineContext): void;
14
+ /**
15
+ * Get the current sync engine context.
16
+ * Returns a safe fallback with no-op implementations if not yet initialized,
17
+ * so SDK files can import at module load time without crashing.
18
+ */
19
+ export declare function getContext(): SyncEngineContext;
20
+ /**
21
+ * Check if the sync engine has been initialized.
22
+ */
23
+ export declare function isSyncEngineInitialized(): boolean;
24
+ /**
25
+ * Reset context (for testing or cleanup).
26
+ */
27
+ export declare function resetSyncEngine(): void;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Module-level context accessor
3
+ *
4
+ * Set once during SDK initialization via `initSyncEngine(context)`.
5
+ * All internal SDK files access dependencies through `getContext()`.
6
+ * This avoids threading context through every constructor.
7
+ */
8
+ import { noopLogger, noopObservability, browserOnlineStatus, defaultSessionErrorDetector, emptyConfig, } from './SyncEngineContext.js';
9
+ let _context = null;
10
+ /**
11
+ * Initialize the sync engine with application-provided dependencies.
12
+ * Must be called before any sync engine operations.
13
+ */
14
+ export function initSyncEngine(context) {
15
+ _context = context;
16
+ }
17
+ /**
18
+ * Get the current sync engine context.
19
+ * Returns a safe fallback with no-op implementations if not yet initialized,
20
+ * so SDK files can import at module load time without crashing.
21
+ */
22
+ export function getContext() {
23
+ if (!_context) {
24
+ return _fallback;
25
+ }
26
+ return _context;
27
+ }
28
+ /**
29
+ * Check if the sync engine has been initialized.
30
+ */
31
+ export function isSyncEngineInitialized() {
32
+ return _context !== null;
33
+ }
34
+ /**
35
+ * Reset context (for testing or cleanup).
36
+ */
37
+ export function resetSyncEngine() {
38
+ _context = null;
39
+ }
40
+ /** Fallback context with no-op implementations */
41
+ const _fallback = {
42
+ logger: noopLogger,
43
+ observability: noopObservability,
44
+ onlineStatus: browserOnlineStatus,
45
+ sessionErrorDetector: defaultSessionErrorDetector,
46
+ config: emptyConfig,
47
+ mutationExecutor: {
48
+ commit: () => Promise.resolve({ lastSyncId: 0 }),
49
+ executeCreate: () => Promise.resolve(),
50
+ executeUpdate: () => Promise.resolve(null),
51
+ executeDelete: () => Promise.resolve(),
52
+ executeArchive: () => Promise.resolve(),
53
+ executeUnarchive: () => Promise.resolve(),
54
+ },
55
+ mutationDispatcher: {
56
+ dispatch: () => Promise.resolve(),
57
+ },
58
+ };
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Ablo Sync Engine - Database Manager
3
+ *
4
+ * Manages the two-tier database architecture:
5
+ * 1. ablo_databases - Metadata about workspace databases
6
+ * 2. ablo_(hash) - Workspace-specific data storage
7
+ *
8
+ * Follows Ablo's architecture for database management.
9
+ */
10
+ export interface DatabaseInfo {
11
+ name: string;
12
+ userId: string;
13
+ workspaceId: string;
14
+ schemaHash: string;
15
+ schemaVersion: number;
16
+ userVersion?: number;
17
+ createdAt: Date;
18
+ updatedAt: Date;
19
+ }
20
+ export interface WorkspaceMetadata {
21
+ lastSyncId: number;
22
+ firstSyncId: number;
23
+ backendDatabaseVersion: number;
24
+ subscribedSyncGroups: string[];
25
+ updatedAt: Date;
26
+ schemaHash?: string;
27
+ syncGroups?: string[];
28
+ versions?: Record<string, number>;
29
+ }
30
+ /**
31
+ * DatabaseManager - Manages Ablo's two-tier database architecture
32
+ *
33
+ * Key responsibilities:
34
+ * - Manages ablo_databases (database registry)
35
+ * - Creates workspace-specific databases (ablo_hash)
36
+ * - Handles database migration and versioning
37
+ * - Provides database info and metadata management
38
+ */
39
+ export declare class DatabaseManager {
40
+ private metaDb;
41
+ private readonly metaDbName;
42
+ constructor();
43
+ /**
44
+ * Initialize the meta database (ablo_databases)
45
+ */
46
+ initializeMetaDatabase(): Promise<void>;
47
+ /**
48
+ * Calculate database info for a user/workspace combination
49
+ */
50
+ calculateDatabaseInfo(userId: string, workspaceId: string, userVersion?: number): Promise<DatabaseInfo>;
51
+ /**
52
+ * Generate deterministic database name
53
+ */
54
+ private generateDatabaseName;
55
+ /**
56
+ * Register database info in ablo_databases
57
+ */
58
+ registerDatabase(info: DatabaseInfo): Promise<void>;
59
+ /**
60
+ * Get database info by name
61
+ */
62
+ getDatabaseInfo(name: string): Promise<DatabaseInfo | null>;
63
+ /**
64
+ * Get all databases for a user
65
+ */
66
+ getDatabasesForUser(userId: string): Promise<DatabaseInfo[]>;
67
+ /**
68
+ * Open workspace-specific database
69
+ */
70
+ openWorkspaceDatabase(dbInfo: DatabaseInfo, createStoresFn?: (db: IDBDatabase, tx: IDBTransaction) => Promise<void>): Promise<IDBDatabase>;
71
+ /**
72
+ * Read workspace metadata from __meta table
73
+ */
74
+ getWorkspaceMetadata(db: IDBDatabase): Promise<WorkspaceMetadata | null>;
75
+ /**
76
+ * Write workspace metadata to __meta table
77
+ */
78
+ setWorkspaceMetadata(db: IDBDatabase, metadata: WorkspaceMetadata): Promise<void>;
79
+ /**
80
+ * Check if a model is persisted (all instances loaded)
81
+ */
82
+ isModelPersisted(db: IDBDatabase, modelName: string): Promise<boolean>;
83
+ /**
84
+ * Mark a model as persisted
85
+ */
86
+ setModelPersisted(db: IDBDatabase, modelName: string, persisted: boolean): Promise<void>;
87
+ /**
88
+ * Get all model persistence states
89
+ */
90
+ getAllModelPersistenceStates(db: IDBDatabase): Promise<Record<string, boolean>>;
91
+ /**
92
+ * Delete a workspace database
93
+ */
94
+ deleteWorkspaceDatabase(dbInfo: DatabaseInfo): Promise<void>;
95
+ /**
96
+ * Get comprehensive database statistics
97
+ */
98
+ getDatabaseStatistics(): Promise<{
99
+ metaDatabaseSize: number;
100
+ totalWorkspaceDatabases: number;
101
+ databasesByUser: Record<string, number>;
102
+ schemaVersions: Record<string, number>;
103
+ }>;
104
+ /**
105
+ * Close all database connections
106
+ */
107
+ close(): Promise<void>;
108
+ }