@abloatai/ablo 0.24.0 → 0.26.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 (246) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +33 -0
  3. package/README.md +45 -36
  4. package/dist/BaseSyncedStore.d.ts +72 -195
  5. package/dist/BaseSyncedStore.js +240 -625
  6. package/dist/Database.d.ts +8 -9
  7. package/dist/Database.js +9 -14
  8. package/dist/LazyReferenceCollection.js +5 -0
  9. package/dist/Model.js +6 -8
  10. package/dist/ModelRegistry.d.ts +25 -6
  11. package/dist/ModelRegistry.js +12 -6
  12. package/dist/NetworkMonitor.js +1 -1
  13. package/dist/ObjectPool.js +9 -3
  14. package/dist/SyncClient.d.ts +15 -5
  15. package/dist/SyncClient.js +152 -66
  16. package/dist/SyncEngineContext.d.ts +1 -3
  17. package/dist/SyncEngineContext.js +1 -2
  18. package/dist/agent/Agent.d.ts +21 -8
  19. package/dist/agent/Agent.js +26 -13
  20. package/dist/ai-sdk/coordination-context.js +9 -3
  21. package/dist/ai-sdk/wrap.d.ts +1 -1
  22. package/dist/ai-sdk/wrap.js +1 -1
  23. package/dist/auth/credentialPolicy.d.ts +10 -2
  24. package/dist/auth/credentialSource.d.ts +1 -13
  25. package/dist/auth/credentialSource.js +6 -13
  26. package/dist/auth/index.js +3 -3
  27. package/dist/cli.cjs +583 -287
  28. package/dist/client/Ablo.d.ts +35 -764
  29. package/dist/client/Ablo.js +64 -688
  30. package/dist/client/ApiClient.d.ts +14 -1
  31. package/dist/client/ApiClient.js +63 -9
  32. package/dist/client/auth.d.ts +8 -17
  33. package/dist/client/auth.js +42 -6
  34. package/dist/client/consoleLogger.d.ts +36 -0
  35. package/dist/client/consoleLogger.js +45 -0
  36. package/dist/client/createInternalComponents.js +1 -1
  37. package/dist/client/createModelProxy.js +10 -6
  38. package/dist/client/credentialEndpoint.d.ts +63 -0
  39. package/dist/client/credentialEndpoint.js +87 -0
  40. package/dist/client/hostedEndpoints.d.ts +24 -0
  41. package/dist/client/hostedEndpoints.js +24 -0
  42. package/dist/client/httpClient.d.ts +1 -1
  43. package/dist/client/identity.js +2 -1
  44. package/dist/client/modelRegistration.d.ts +14 -0
  45. package/dist/client/modelRegistration.js +321 -0
  46. package/dist/client/options.d.ts +396 -0
  47. package/dist/client/options.js +10 -0
  48. package/dist/client/resourceTypes.d.ts +344 -0
  49. package/dist/client/resourceTypes.js +13 -0
  50. package/dist/client/schemaConfig.d.ts +56 -0
  51. package/dist/client/schemaConfig.js +188 -0
  52. package/dist/client/sessionMint.d.ts +1 -1
  53. package/dist/client/validateAbloOptions.js +1 -1
  54. package/dist/client/wsMutationExecutor.d.ts +33 -0
  55. package/dist/client/wsMutationExecutor.js +77 -0
  56. package/dist/context.js +0 -3
  57. package/dist/coordination/index.d.ts +8 -1
  58. package/dist/coordination/index.js +24 -1
  59. package/dist/coordination/trace.d.ts +0 -1
  60. package/dist/coordination/trace.js +0 -1
  61. package/dist/core/DatabaseManager.d.ts +0 -1
  62. package/dist/core/DatabaseManager.js +26 -24
  63. package/dist/core/QueryProcessor.js +7 -8
  64. package/dist/core/QueryView.d.ts +14 -2
  65. package/dist/core/QueryView.js +4 -5
  66. package/dist/core/StoreManager.d.ts +2 -2
  67. package/dist/core/StoreManager.js +5 -1
  68. package/dist/core/index.d.ts +1 -1
  69. package/dist/core/openIDBWithTimeout.js +15 -11
  70. package/dist/core/query-utils.js +1 -1
  71. package/dist/core/storeContract.d.ts +143 -0
  72. package/dist/core/storeContract.js +16 -0
  73. package/dist/errorCodes.d.ts +11 -2
  74. package/dist/errorCodes.js +155 -137
  75. package/dist/errors.d.ts +18 -7
  76. package/dist/errors.js +14 -1
  77. package/dist/index.js +5 -0
  78. package/dist/interfaces/index.d.ts +6 -20
  79. package/dist/keys/index.js +18 -9
  80. package/dist/mutators/UndoManager.js +6 -1
  81. package/dist/mutators/defineMutators.d.ts +1 -3
  82. package/dist/policy/types.d.ts +1 -1
  83. package/dist/query/client.d.ts +11 -0
  84. package/dist/query/client.js +81 -52
  85. package/dist/react/AbloProvider.d.ts +6 -10
  86. package/dist/react/AbloProvider.js +22 -15
  87. package/dist/react/context.d.ts +2 -118
  88. package/dist/react/useAblo.js +1 -1
  89. package/dist/react/useErrorListener.js +1 -1
  90. package/dist/react/useMutationFailureListener.js +1 -1
  91. package/dist/react/useMutators.js +7 -1
  92. package/dist/react/useReactive.js +0 -1
  93. package/dist/react/useUndoScope.js +2 -2
  94. package/dist/schema/ddlLock.d.ts +39 -0
  95. package/dist/schema/ddlLock.js +51 -0
  96. package/dist/schema/field.js +1 -1
  97. package/dist/schema/index.d.ts +4 -3
  98. package/dist/schema/index.js +15 -5
  99. package/dist/schema/model.d.ts +3 -3
  100. package/dist/schema/model.js +2 -2
  101. package/dist/schema/openapi.js +5 -1
  102. package/dist/schema/queries.d.ts +1 -1
  103. package/dist/schema/queries.js +1 -0
  104. package/dist/schema/residency.d.ts +35 -0
  105. package/dist/schema/residency.js +27 -0
  106. package/dist/schema/roles.d.ts +25 -0
  107. package/dist/schema/roles.js +37 -0
  108. package/dist/schema/schema.d.ts +3 -3
  109. package/dist/schema/schema.js +11 -7
  110. package/dist/schema/serialize.d.ts +8 -8
  111. package/dist/schema/sync-delta-row.d.ts +2 -2
  112. package/dist/schema/sync-delta-row.js +2 -2
  113. package/dist/server/storage-mode.d.ts +8 -1
  114. package/dist/server/storage-mode.js +14 -4
  115. package/dist/source/adapter.d.ts +1 -1
  116. package/dist/source/adapters/drizzle.js +4 -3
  117. package/dist/source/adapters/kysely.js +4 -3
  118. package/dist/source/adapters/memory.js +1 -1
  119. package/dist/source/adapters/prisma.js +5 -4
  120. package/dist/source/conformance.js +10 -8
  121. package/dist/source/connector.js +4 -2
  122. package/dist/source/factory.d.ts +106 -0
  123. package/dist/source/factory.js +273 -0
  124. package/dist/source/index.d.ts +11 -461
  125. package/dist/source/index.js +14 -416
  126. package/dist/source/next.d.ts +1 -1
  127. package/dist/source/next.js +1 -1
  128. package/dist/source/pushQueue.d.ts +1 -1
  129. package/dist/source/pushQueue.js +6 -3
  130. package/dist/source/signing.d.ts +59 -0
  131. package/dist/source/signing.js +142 -0
  132. package/dist/source/types.d.ts +334 -0
  133. package/dist/source/types.js +44 -0
  134. package/dist/stores/ObjectStore.d.ts +1 -1
  135. package/dist/stores/ObjectStore.js +23 -23
  136. package/dist/stores/SyncActionStore.d.ts +1 -1
  137. package/dist/stores/SyncActionStore.js +70 -35
  138. package/dist/surface.d.ts +1 -1
  139. package/dist/surface.js +1 -0
  140. package/dist/sync/AreaOfInterestManager.d.ts +2 -2
  141. package/dist/sync/AreaOfInterestManager.js +1 -1
  142. package/dist/sync/BootstrapHelper.d.ts +1 -3
  143. package/dist/sync/BootstrapHelper.js +29 -8
  144. package/dist/sync/ConnectionManager.d.ts +8 -0
  145. package/dist/sync/ConnectionManager.js +19 -4
  146. package/dist/sync/HydrationCoordinator.d.ts +12 -0
  147. package/dist/sync/HydrationCoordinator.js +25 -5
  148. package/dist/sync/NetworkProbe.d.ts +3 -2
  149. package/dist/sync/NetworkProbe.js +7 -6
  150. package/dist/sync/SyncWebSocket.d.ts +71 -120
  151. package/dist/sync/SyncWebSocket.js +324 -630
  152. package/dist/sync/awaitClaimGrant.js +27 -19
  153. package/dist/sync/bootstrapApply.d.ts +60 -0
  154. package/dist/sync/bootstrapApply.js +65 -0
  155. package/dist/sync/commitFrames.d.ts +43 -0
  156. package/dist/sync/commitFrames.js +94 -0
  157. package/dist/sync/createClaimStream.js +3 -2
  158. package/dist/sync/createSnapshot.d.ts +1 -3
  159. package/dist/sync/credentialLifecycle.d.ts +175 -0
  160. package/dist/sync/credentialLifecycle.js +329 -0
  161. package/dist/sync/deltaPipeline.d.ts +111 -0
  162. package/dist/sync/deltaPipeline.js +262 -0
  163. package/dist/sync/groupChange.d.ts +111 -0
  164. package/dist/sync/groupChange.js +246 -0
  165. package/dist/sync/heartbeat.d.ts +62 -0
  166. package/dist/sync/heartbeat.js +91 -0
  167. package/dist/sync/participants.d.ts +8 -8
  168. package/dist/sync/syncCursor.d.ts +44 -0
  169. package/dist/sync/syncCursor.js +59 -0
  170. package/dist/sync/syncPlan.d.ts +62 -0
  171. package/dist/sync/syncPlan.js +51 -0
  172. package/dist/sync/wsFrameHandlers.d.ts +117 -0
  173. package/dist/sync/wsFrameHandlers.js +378 -0
  174. package/dist/testing/fixtures/bootstrap.d.ts +9 -11
  175. package/dist/testing/fixtures/deltas.d.ts +1 -1
  176. package/dist/testing/fixtures/models.js +1 -0
  177. package/dist/testing/helpers/sync-engine-harness.d.ts +0 -3
  178. package/dist/testing/helpers/sync-engine-harness.js +2 -1
  179. package/dist/testing/index.d.ts +1 -1
  180. package/dist/testing/index.js +1 -1
  181. package/dist/testing/mocks/MockSyncContext.d.ts +1 -17
  182. package/dist/testing/mocks/MockSyncContext.js +1 -32
  183. package/dist/testing/mocks/MockSyncStore.d.ts +4 -4
  184. package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
  185. package/dist/transactions/TransactionQueue.d.ts +20 -56
  186. package/dist/transactions/TransactionQueue.js +101 -487
  187. package/dist/transactions/TransactionStore.d.ts +18 -0
  188. package/dist/transactions/TransactionStore.js +51 -0
  189. package/dist/transactions/coalesceRules.d.ts +34 -0
  190. package/dist/transactions/coalesceRules.js +117 -0
  191. package/dist/transactions/commitPayload.d.ts +134 -0
  192. package/dist/transactions/commitPayload.js +152 -0
  193. package/dist/transactions/deltaConfirmation.d.ts +60 -0
  194. package/dist/transactions/deltaConfirmation.js +223 -0
  195. package/dist/transactions/optimistic.d.ts +24 -0
  196. package/dist/transactions/optimistic.js +45 -0
  197. package/dist/transactions/persistedReplay.d.ts +93 -0
  198. package/dist/transactions/persistedReplay.js +105 -0
  199. package/dist/types/index.d.ts +3 -3
  200. package/dist/types/index.js +0 -5
  201. package/dist/types/modelData.d.ts +12 -0
  202. package/dist/types/modelData.js +11 -0
  203. package/dist/types/participant.d.ts +21 -0
  204. package/dist/types/participant.js +12 -0
  205. package/dist/types/streams.d.ts +8 -14
  206. package/dist/utils/mobx-setup.d.ts +1 -1
  207. package/dist/utils/mobx-setup.js +2 -2
  208. package/dist/wire/errorEnvelope.d.ts +11 -2
  209. package/dist/wire/errorEnvelope.js +14 -2
  210. package/dist/wire/frames.d.ts +90 -0
  211. package/dist/wire/frames.js +55 -1
  212. package/dist/wire/index.d.ts +4 -1
  213. package/dist/wire/index.js +20 -1
  214. package/dist/wire/protocol.d.ts +45 -0
  215. package/dist/wire/protocol.js +45 -0
  216. package/dist/wire/protocolVersion.d.ts +56 -0
  217. package/dist/wire/protocolVersion.js +63 -0
  218. package/docs/api-keys.md +4 -3
  219. package/docs/data-sources.md +12 -5
  220. package/docs/examples/existing-python-backend.md +3 -3
  221. package/docs/identity.md +4 -4
  222. package/docs/integration-guide.md +1 -1
  223. package/docs/react.md +1 -1
  224. package/docs/sessions.md +5 -7
  225. package/llms.txt +4 -2
  226. package/package.json +15 -13
  227. package/dist/client/index.d.ts +0 -36
  228. package/dist/client/index.js +0 -33
  229. package/dist/config/index.d.ts +0 -10
  230. package/dist/config/index.js +0 -12
  231. package/dist/interfaces/headless.d.ts +0 -95
  232. package/dist/interfaces/headless.js +0 -41
  233. package/dist/query/index.d.ts +0 -6
  234. package/dist/query/index.js +0 -5
  235. package/dist/realtime/index.d.ts +0 -10
  236. package/dist/realtime/index.js +0 -9
  237. package/dist/schema/plane.d.ts +0 -23
  238. package/dist/schema/plane.js +0 -19
  239. package/dist/server/next.d.ts +0 -51
  240. package/dist/server/next.js +0 -47
  241. package/dist/sync/OfflineFlush.d.ts +0 -9
  242. package/dist/sync/OfflineFlush.js +0 -22
  243. package/dist/sync/OfflineTransactionStore.d.ts +0 -37
  244. package/dist/sync/OfflineTransactionStore.js +0 -263
  245. package/dist/transactions/index.d.ts +0 -16
  246. package/dist/transactions/index.js +0 -7
@@ -5,7 +5,7 @@
5
5
  * bootstrap, offline queue, DI adapters) behind a single function call.
6
6
  *
7
7
  * Usage:
8
- * import { Ablo } from '@abloatai/ablo/client';
8
+ * import { Ablo } from '@abloatai/ablo';
9
9
  * import { schema } from './schema';
10
10
  *
11
11
  * const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
@@ -18,712 +18,22 @@
18
18
  * });
19
19
  * await sync.reports.delete({ id: reportId });
20
20
  */
21
- import type { StaleNotification, ReadDependency } from '../coordination/schema.js';
22
21
  import type { Schema, SchemaRecord, InferModel, InferCreate } from '../schema/schema.js';
23
- import type { SyncEngineConfig, SyncLogger, MutationExecutor, MutationDispatcher, SyncObservabilityProvider, SyncAnalytics, SessionErrorDetector, OnlineStatusProvider } from '../interfaces/index.js';
24
22
  import type { ModelTarget, ModelClaim } from '../coordination/schema.js';
25
23
  export type { ModelTarget, ModelClaim };
26
24
  import { ObjectPool } from '../ObjectPool.js';
27
25
  import type { SyncStoreContract } from '../react/context.js';
28
26
  import type { SyncWebSocket } from '../sync/SyncWebSocket.js';
29
- import type { SyncGroupInput } from '../schema/roles.js';
30
27
  import { type SyncStatus } from '../BaseSyncedStore.js';
31
- import type { ModelUpdater, ContentionOptions } from './functionalUpdate.js';
32
- import type { ClaimStream, ClaimWaitOptions, PresenceStream, Snapshot } from '../types/streams.js';
33
- import type { Claim, HeldClaim, Duration } from '../types/streams.js';
28
+ import type { PresenceStream, Snapshot } from '../types/streams.js';
34
29
  import { type AbloApi, type AbloApiClientOptions, type AbloApiClaims } from './ApiClient.js';
35
30
  import { type AbloHttpClient, type AbloHttpClientOptions } from './httpClient.js';
36
- /**
37
- * Async function that resolves an apiKey at request time. Use for
38
- * credential rotation rotate from a vault, refresh from session
39
- * storage, or pull from a Better Auth session. Mirrors Anthropic's
40
- * `ApiKeySetter` exactly so any rotation pattern that works with
41
- * `@anthropic-ai/sdk` works here.
42
- *
43
- * Re-exported from `./auth` so existing import paths (`@abloatai/ablo`)
44
- * keep resolving; the canonical definition lives there alongside the
45
- * resolvers that consume it.
46
- */
47
- export type { ApiKeySetter } from './auth.js';
48
- import type { ApiKeySetter } from './auth.js';
49
- import { type AbloPersistence } from './persistence.js';
50
- /**
51
- * Options for `Ablo({...})`.
52
- *
53
- * The only required field is `schema`. The default path is one line:
54
- *
55
- * ```ts
56
- * const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
57
- * ```
58
- *
59
- * `apiKey` itself defaults to `process.env.ABLO_API_KEY`, so in most
60
- * server setups `Ablo({ schema })` is enough. Every other field is
61
- * optional tuning (timeouts, retries, custom fetch, persistence) —
62
- * if you're not sure whether you need one, you don't. Reach for them
63
- * the way you'd reach for the equivalent option on the Stripe / OpenAI
64
- * / Anthropic clients: rarely, and deliberately.
65
- *
66
- * @see https://docs.abloatai.com — full option reference
67
- */
68
- export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
69
- /**
70
- * TypeScript schema defined with `defineSchema()`. Required — it's what
71
- * makes `ablo.weatherReports.update(...)` typed. This is the one field you must
72
- * pass; start here.
73
- */
74
- schema: Schema<S>;
75
- /**
76
- * API key — **the one auth field most apps set.** Server-side this is your
77
- * secret `sk_` (and it defaults to `process.env['ABLO_API_KEY']`, so you
78
- * usually pass nothing). A long-lived key needs no refresh; the client uses
79
- * it as-is.
80
- *
81
- * Accepts a static string OR an async `() => Promise<string | null>` resolver
82
- * — the single credential path. Use the resolver form for two cases:
83
- *
84
- * - **Key rotation** (server): pull a fresh `sk_`/`pk_` from a vault on each
85
- * bootstrap (AWS STS, GCP IAM, Vault).
86
- * - **Short-lived per-user browser** auth: return the fresh `ek_`/`rk_` bearer
87
- * your backend minted for the signed-in user. The client mints once before
88
- * connect, then keeps it fresh for you — a refresh timer ahead of expiry
89
- * plus re-mint on OS-wake / network-online / tab-focus, and a reactive
90
- * re-mint when a probe finds the key stale. You never call a refresh method
91
- * (Supabase `autoRefreshToken` model).
92
- *
93
- * Resolver contract: resolve a token; resolve `null` when the login itself is
94
- * gone (terminal → the client signs out / fails `ready()` with `session_expired`);
95
- * or THROW on a transient failure (→ back off and retry, never sign out). A
96
- * static string never refreshes — it is used as-is.
97
- */
98
- apiKey?: string | ApiKeySetter | null | undefined;
99
- /**
100
- * @deprecated The direct connector lets Ablo dial INTO your Postgres and write to
101
- * it — the operate-their-database posture we are moving off. Ablo is Stripe-shaped:
102
- * it hosts only the transaction log (the ordered sync_deltas) + coordination, never
103
- * your data; your rows always live in your own database. Use the signed Data Source
104
- * endpoint instead — keep `DATABASE_URL` in your app, expose `dataSource(...)`, and
105
- * let your server own the write while Ablo coordinates the sync stream. To keep the
106
- * log in your infra too, self-host the engine. See
107
- * docs/plans/stripe-shaped-storage-posture.md.
108
- *
109
- * Still honored at runtime for back-compat. SERVER-ONLY: it carries credentials, so
110
- * it is never sent from the browser — constructing a client with `databaseUrl` and
111
- * `dangerouslyAllowBrowser` throws. If you use it, provide a NON-superuser,
112
- * non-`BYPASSRLS` role; the connector rejects privileged roles that cannot enforce RLS.
113
- */
114
- databaseUrl?: string | null | undefined;
115
- /**
116
- * Local persistence mode. Pass `indexeddb` only when you want offline
117
- * queueing and a reload-surviving browser cache.
118
- *
119
- * @default 'memory'
120
- */
121
- persistence?: AbloPersistence;
122
- /**
123
- * Transport selector. `'websocket'` (default) is the live client —
124
- * persistent socket, local synced pool, `onChange` subscriptions. `'http'`
125
- * returns the STATELESS client for server-side actors (agents, workers,
126
- * serverless): same `ablo.<model>` surface and coordination plane, but each
127
- * call is one HTTP round-trip, identity rides the Bearer credential, and no
128
- * socket is ever opened. With `'http'` the return type narrows to
129
- * `AbloHttpClient<S>`, so stateful-only capabilities (`get`/`getAll`,
130
- * `onChange`) are compile errors rather than latent runtime gaps.
131
- *
132
- * Note: session/credential minting (`sessions.create`) currently runs on the
133
- * stateful (default) client, not the http client.
134
- *
135
- * @default 'websocket'
136
- */
137
- transport?: 'websocket' | 'http' | undefined;
138
- /**
139
- * Turn Ablo's diagnostic logging on/off. `true` surfaces the `[Ablo]`
140
- * coordination trace — claims requested / queued / granted / released, agent
141
- * handovers, connection state — so you can SEE the human+agent coordination
142
- * you built while debugging. Omitted/`false` keeps the quiet default (only
143
- * warnings + errors). For a middle ground use {@link logLevel}. Env override:
144
- * `ABLO_LOG_LEVEL`. Ignored if a custom logger is supplied.
145
- */
146
- debug?: boolean | undefined;
147
- /**
148
- * Log threshold for the default `[Ablo]` logger (takes precedence over
149
- * {@link debug}). `'info'` = coordination + connection events without the
150
- * per-model registration firehose; `'debug'` = everything; `'warn'` (default)
151
- * = warnings + errors only; `'silent'` = nothing. Env override: `ABLO_LOG_LEVEL`.
152
- */
153
- logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined;
154
- /**
155
- * Bearer auth token. Hosted-cloud consumers pass `apiKey`; self-hosted
156
- * deployments may pass a bearer token minted by their own auth layer.
157
- */
158
- authToken?: string | null | undefined;
159
- /**
160
- * Override the Ablo API base URL. Defaults to hosted production.
161
- */
162
- baseURL?: string | null | undefined;
163
- /** Custom fetch implementation for tests, proxies, or non-standard runtimes. */
164
- fetch?: typeof fetch | undefined;
165
- /** Default headers sent with every API request. */
166
- defaultHeaders?: Record<string, string | null | undefined> | undefined;
167
- /** Default query parameters sent with every API request. */
168
- defaultQuery?: Record<string, string | undefined> | undefined;
169
- /**
170
- * Client-side use is disabled by default because private API keys should
171
- * not ship to browsers. Set this only when the browser holds a minted
172
- * session token (`ek_`/`rk_`) or you route through a controlled server proxy.
173
- */
174
- dangerouslyAllowBrowser?: boolean | undefined;
175
- }
176
- export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
177
- /**
178
- * API key used for authentication.
179
- *
180
- * Accepts a static string (`sk_live_...`) or an async function that
181
- * resolves to one. Defaults to `process.env['ABLO_API_KEY']`.
182
- *
183
- * When a function is provided, it's invoked before each request so
184
- * you can rotate or refresh credentials at runtime. The function
185
- * must return a non-empty string; otherwise an `AbloAuthenticationError`
186
- * is thrown. If the function throws, the error is wrapped with the
187
- * original available as `cause`.
188
- *
189
- * Mirrors Anthropic / OpenAI / Stripe SDK shape exactly.
190
- */
191
- apiKey?: string | ApiKeySetter | null | undefined;
192
- /**
193
- * Bearer auth token. Sent as `Authorization: Bearer <token>` on
194
- * every request.
195
- *
196
- * Use this for self-hosted deployments where your auth layer mints
197
- * cap tokens directly. Hosted-cloud consumers pass `apiKey` instead;
198
- * the server handles cap-mint internally.
199
- */
200
- authToken?: string | null | undefined;
201
- /**
202
- * Override the default base URL. Defaults to
203
- * `wss://api.abloatai.com` for hosted production; pass an explicit
204
- * URL for self-hosted or private deployments.
205
- */
206
- baseURL?: string | null | undefined;
207
- /**
208
- * Custom `fetch` implementation. Defaults to `globalThis.fetch`.
209
- * Override for testing, custom transports, or runtime shims.
210
- */
211
- fetch?: typeof fetch | undefined;
212
- /**
213
- * Default headers to include with every request to the API.
214
- * Removed per-request by setting the header to `null` in request
215
- * options.
216
- */
217
- defaultHeaders?: Record<string, string | null | undefined> | undefined;
218
- /**
219
- * Default query parameters to include with every request.
220
- * Removed per-request by setting the param to `undefined`.
221
- */
222
- defaultQuery?: Record<string, string | undefined> | undefined;
223
- /**
224
- * Client-side use of this SDK is disabled by default — your apiKey
225
- * would ship to every visitor's network tab. Only set this to
226
- * `true` if you've understood the risk and have appropriate
227
- * mitigations (a minted session token, a server-side proxy, etc).
228
- */
229
- dangerouslyAllowBrowser?: boolean | undefined;
230
- /**
231
- * TypeScript schema defined with `defineSchema()`.
232
- *
233
- * The root `Ablo(...)` client is schema-first so consumers get typed
234
- * model clients such as `ablo.weatherReports.update(...)`. Omit `schema`
235
- * only for the advanced Model / Claim / Commit client.
236
- */
237
- schema: Schema<S>;
238
- /**
239
- * @deprecated Server derives participant kind from the apiKey's
240
- * scope. Pass apiKey only; this option will be removed once the
241
- * server-internal cap-mint flow lands.
242
- */
243
- kind?: 'user' | 'agent' | 'system';
244
- /**
245
- * @deprecated Server derives user identity from the apiKey's
246
- * scope (or from `Ablo-Acting-User` request header for B2B2C).
247
- * Removed once Phase 3 ships.
248
- */
249
- user?: {
250
- id: string;
251
- teamIds?: string[];
252
- };
253
- /**
254
- * @deprecated Server derives agent identity from the apiKey's
255
- * scope. Removed once Phase 3 ships.
256
- */
257
- agentId?: string;
258
- /**
259
- * @deprecated Cap-mint moves server-internal in Phase 3. Pass
260
- * `apiKey` only; the server handles capability issuance.
261
- */
262
- capabilityToken?: string;
263
- /** Custom logger (default: console). Supplying one bypasses {@link debug}/{@link logLevel}. */
264
- logger?: SyncLogger;
265
- /**
266
- * Turn Ablo's diagnostic logging on/off. `true` surfaces the `[Ablo]`
267
- * coordination trace — claims acquired / queued / granted / released, agent
268
- * handovers, connection state — plus internal lifecycle, so you can SEE the
269
- * human+agent coordination you built. Omitted/`false` keeps the quiet default
270
- * (only warnings + errors). For a middle ground use {@link logLevel}.
271
- * Env override: `ABLO_LOG_LEVEL`. Ignored if a custom {@link logger} is passed.
272
- */
273
- debug?: boolean;
274
- /**
275
- * Log threshold for the default `[Ablo]` logger (takes precedence over
276
- * {@link debug}). `'info'` = coordination + connection events without the
277
- * per-model registration firehose; `'debug'` = everything; `'warn'` (default)
278
- * = warnings + errors only; `'silent'` = nothing. Env override: `ABLO_LOG_LEVEL`.
279
- */
280
- logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
281
- /** ObjectPool size limit (default: 10000) */
282
- maxPoolSize?: number;
283
- /**
284
- * Local persistence mode. Defaults to `memory` so Ablo behaves like a
285
- * point solution for shared state instead of silently bolting IndexedDB
286
- * durability onto every browser consumer.
287
- *
288
- * Pass `persistence: 'indexeddb'` only when you want offline queueing
289
- * and a reload-surviving local cache in a browser.
290
- */
291
- persistence?: AbloPersistence;
292
- /** @deprecated Use `persistence: 'indexeddb'` for durable browser storage. */
293
- offline?: boolean;
294
- /**
295
- * @deprecated Internal/testing escape hatch. Use `persistence` in
296
- * production code. `true` maps to `memory`; `false` maps to
297
- * `indexeddb` in browsers.
298
- */
299
- inMemory?: boolean;
300
- /**
301
- * If true, initialization starts immediately in the background so
302
- * `sync.reports.findMany()` works after `await sync.ready()`.
303
- *
304
- * If false (default), the consumer MUST call `await sync.ready()` before
305
- * using the engine — any query before that returns empty results.
306
- *
307
- * Default: false (explicit is better — prevents silent init failures).
308
- */
309
- autoStart?: boolean;
310
- /**
311
- * How aggressively this client should pull baseline state at
312
- * startup.
313
- *
314
- * - `'full'`: pull every delta in the configured sync groups before
315
- * `ready()` resolves. Default for `kind: 'user'`.
316
- * - `'none'`: open the WS and process live deltas only — no baseline
317
- * fetch. Reads round-trip via `model.retrieve()`; subscriptions
318
- * populate the pool lazily via covering deltas. Default for
319
- * `kind: 'agent'` because agent-worker / routine runners don't
320
- * need (or want) a local replica of the org's tenant plane.
321
- */
322
- bootstrapMode?: 'full' | 'none';
323
- /**
324
- * Custom observability provider (Sentry, Honeycomb, OTel, etc.).
325
- * Default: a noop implementation that drops all breadcrumbs and spans.
326
- */
327
- observability?: SyncObservabilityProvider;
328
- /**
329
- * Custom analytics provider (PostHog, Amplitude, Segment, etc.).
330
- * Default: a noop implementation that drops all events.
331
- */
332
- analytics?: SyncAnalytics;
333
- /**
334
- * Detect whether an error from a mutation/bootstrap response means the
335
- * user's session has expired. Used to surface re-auth prompts. Default:
336
- * heuristic that matches `401 Unauthorized` and a few common error shapes.
337
- */
338
- sessionErrorDetector?: SessionErrorDetector;
339
- /**
340
- * Detect whether the browser is currently online. Default: reads
341
- * `navigator.onLine` and listens to the `online`/`offline` events.
342
- */
343
- onlineStatus?: OnlineStatusProvider;
344
- /**
345
- * Replace the built-in `MutationExecutor` (which posts a hardcoded
346
- * `commit` method against `${url}/graphql`) with one that uses your own
347
- * GraphQL client, auth headers, retry policy, and observability hooks.
348
- *
349
- * Default: a fetch-based executor that targets `${url}/graphql` and sends
350
- * the configured bearer (`apiKey` / backend-minted token) as `Authorization`.
351
- */
352
- mutationExecutor?: MutationExecutor;
353
- /**
354
- * Replace the built-in `MutationDispatcher` (used by the offline queue
355
- * to replay mutations on reconnect). If you override `mutationExecutor`
356
- * you almost always want to override this too so the two paths share
357
- * the same auth/retry behavior.
358
- *
359
- * Default: a thin dispatcher that routes to the built-in executor.
360
- */
361
- mutationDispatcher?: MutationDispatcher;
362
- /**
363
- * Partial overrides for the auto-derived `SyncEngineConfig`. Merged on
364
- * top of `deriveConfigFromSchema(schema)`. Use this when you need
365
- * specific `modelCreatePriority`, `batchableModels`, or
366
- * `essentialFields` settings that the schema cannot express.
367
- */
368
- configOverrides?: Partial<SyncEngineConfig>;
369
- /**
370
- * Sync groups (entity scopes) this client subscribes to. **Provisional, not
371
- * deprecated** — pick the right lane: normally the server derives these from
372
- * the apiKey's scope, but passing them is still REQUIRED today in any config
373
- * where the key doesn't resolve them (omitting yields a `degenerate
374
- * syncGroups` warning and a zero-fan-out client). Keep passing it explicitly
375
- * until the server-derived path ships in Phase 3, at which point it becomes a
376
- * true no-op and is removed. Build values with `syncGroup(kind, id)` from
377
- * `@abloatai/ablo/schema`.
378
- */
379
- syncGroups?: string[];
380
- /**
381
- * Override the bootstrap endpoint base URL. Use this when your sync
382
- * server's HTTP API lives on a different host than the WebSocket URL.
383
- *
384
- * Must include the `/api` prefix — `BootstrapHelper` appends
385
- * `/sync/bootstrap` directly. Example:
386
- * `'http://api.example.com/api'` → `http://api.example.com/api/sync/bootstrap`.
387
- *
388
- * Default: `${url.replace(/^ws/, 'http')}/api`.
389
- */
390
- bootstrapBaseUrl?: string;
391
- /**
392
- * Ablo-owned account scope. Required for Branch 3 identity resolution
393
- * in `identity.ts` — without it the SDK falls through to the
394
- * `/api/identity` HTTP-derived path (Branch 2).
395
- */
396
- organizationId?: string;
397
- }
398
- /**
399
- * Operations available on each model in the sync engine.
400
- *
401
- * Naming aligns with Stripe / OpenAI / Anthropic conventions:
402
- * `retrieve({ id })` — async single-row server read
403
- * `list({ where })` — async collection server read
404
- * `get(id)` / `getAll(...)` / `getCount(...)` — local graph snapshots
405
- * `create({ data })` / `update({ id, data })` / `delete({ id })` — writes
406
- * `claim({ id })` — durable claim handle for coordinated writes
407
- */
408
- export type { LocalCountOptions, LocalReadOptions, ModelListScope, ServerReadOptions, ModelRetrieveParams, ModelCreateParams, ModelUpdateParams, ModelDeleteParams, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, Claim, HeldClaim, ModelOperations, } from './createModelProxy.js';
409
- import type { ModelOperations, ClaimOptions, ClaimParams, ClaimReadApi, AwaitedClaimMethod, ServerReadOptions } from './createModelProxy.js';
410
- export type ModelOperationAction = 'create' | 'update' | 'delete' | 'archive' | 'unarchive';
411
- export type CommitWait = 'queued' | 'confirmed';
412
- export interface ModelRead<T = Record<string, unknown>> {
413
- /**
414
- * The row, or `undefined` when no row matched the id (or it's outside the
415
- * caller's scope). A miss is data-absence, not an error — `retrieve` never
416
- * throws "not found", mirroring the WebSocket client's `T | undefined`.
417
- * Branch on it: `const deal = (await ablo.deals.retrieve({ id })).data; if (!deal) …`.
418
- */
419
- readonly data: T | undefined;
420
- readonly stamp: number;
421
- readonly claims: readonly ModelClaim[];
422
- }
423
- export type IfClaimedPolicy = 'return' | 'fail';
424
- export interface ClaimedOptions {
425
- /**
426
- * What to do when another participant has claimed the target: `return`
427
- * includes active claim metadata in the response; `fail` throws
428
- * `AbloClaimedError`. Waiting for a claim to clear is a claim-side concern —
429
- * take `ablo.<model>.claim({ id })` (it queues fairly); reads never block.
430
- */
431
- readonly ifClaimed?: IfClaimedPolicy;
432
- }
433
- export type { ClaimWaitOptions } from '../types/streams.js';
434
- export interface ModelReadOptions extends ClaimedOptions {
435
- }
436
- export interface ClaimCreateOptions {
437
- readonly target: ModelTarget;
438
- /** Human-readable phase shown to peers — `'editing'`, `'writing'`. The same
439
- * word on every claim surface. */
440
- readonly reason: string;
441
- readonly ttl?: Duration;
442
- /**
443
- * Join the server's fair FIFO queue when the target is already claimed,
444
- * rather than failing immediately. `create` then resolves only once the
445
- * lease is actually ours (the server pushes `claim_acquired` if the target
446
- * was free, or `claim_granted` when we reach the head of the line). Without
447
- * this, a contended claim throws. Used by `ablo.<model>.claim` so writers
448
- * serialize instead of racing.
449
- */
450
- readonly queue?: boolean;
451
- /** Cap on how long to wait for a queued grant before rejecting. */
452
- readonly waitTimeoutMs?: number;
453
- /**
454
- * Backpressure: reject with `AbloClaimedError('queue_too_deep')` instead of
455
- * waiting if the queue is already `>= maxQueueDepth` when we join.
456
- */
457
- readonly maxQueueDepth?: number;
458
- }
459
- export interface CommitOperationInput {
460
- readonly action: ModelOperationAction;
461
- /** The model name — matches `ablo.<model>` and the schema's `model()`. */
462
- readonly model?: string;
463
- readonly target?: ModelTarget;
464
- readonly id?: string | null;
465
- readonly data?: Record<string, unknown> | null;
466
- readonly transactionId?: string | null;
467
- readonly readAt?: number | null;
468
- readonly onStale?: 'reject' | 'overwrite' | 'notify' | null;
469
- }
470
- export interface CommitCreateOptions {
471
- readonly claimRef?: string | {
472
- readonly id: string;
473
- } | null;
474
- readonly idempotencyKey?: string | null;
475
- readonly readAt?: number | null;
476
- readonly onStale?: 'reject' | 'overwrite' | 'notify' | null;
477
- /**
478
- * A claim handle from `ablo.<model>.claim({ id })` (or the HTTP claim
479
- * surface). Same vocabulary as the per-model writes: the handle's
480
- * snapshot watermark becomes the batch `readAt` default and `onStale`
481
- * defaults to `'reject'`, so a commit that follows a claim is guarded
482
- * against concurrent edits without re-stating the watermark by hand.
483
- * Explicit `readAt`/`onStale` on the options win.
484
- */
485
- readonly claim?: Claim<Record<string, unknown>> | null;
486
- readonly operation?: CommitOperationInput;
487
- readonly operations?: readonly CommitOperationInput[];
488
- readonly wait?: CommitWait;
489
- /**
490
- * Batch-level read dependencies (the STORM "did anything I looked at change?"
491
- * layer). Declare the rows (`{model,id,readAt,fields?}`) or sync groups
492
- * (`{group,readAt}`, e.g. `deck:abc`) this batch was premised on; the server
493
- * validates none moved since `readAt` and fires the entry's `onStale` over the
494
- * batch. Distinct from the write-target `readAt` — this guards what you READ,
495
- * not what you write.
496
- */
497
- readonly reads?: readonly ReadDependency[] | null;
498
- }
499
- export interface CommitReceipt {
500
- readonly id: string;
501
- readonly status: CommitWait;
502
- readonly lastSyncId?: number;
503
- /**
504
- * Stale-context notifications (notify-instead-of-abort, non-coercion). Present
505
- * only when this commit guarded a write with `onStale: 'notify' and
506
- * the premise moved concurrently — the conflicting field's current value,
507
- * handed back as data instead of a forced `AbloStaleContextError`. The engine
508
- * surfaces state; the intelligent actor (agent or human) decides how to
509
- * resolve. Also fires on `conflict:notified`.
510
- */
511
- readonly notifications?: readonly StaleNotification[];
512
- /**
513
- * Ids of UPDATE/DELETE targets in this commit that matched ZERO rows (the row
514
- * doesn't exist, or is outside the caller's org). Present (non-empty) only
515
- * when a write missed. Typed resource wrappers turn this into a loud
516
- * `AbloNotFoundError`; a raw `commits.create` caller can inspect it directly.
517
- */
518
- readonly missingIds?: readonly string[];
519
- }
520
- export interface CommitResource {
521
- create(options: CommitCreateOptions): Promise<CommitReceipt>;
522
- }
523
- export interface ClaimResource extends ClaimStream {
524
- create(options: ClaimCreateOptions): Promise<Claim>;
525
- list(target?: Partial<ModelTarget>): readonly ModelClaim[];
526
- waitFor(target: Partial<ModelTarget>, options?: ClaimWaitOptions): Promise<void>;
527
- }
528
- export interface ModelMutationOptions extends ClaimedOptions {
529
- readonly claimRef?: string | {
530
- readonly id: string;
531
- } | null;
532
- readonly idempotencyKey?: string | null;
533
- readonly readAt?: number | null;
534
- readonly onStale?: 'reject' | 'overwrite' | 'notify' | null;
535
- readonly wait?: CommitWait;
536
- readonly claim?: Claim | ClaimOptions | null;
537
- }
538
- /**
539
- * The HTTP/stateless claim surface. Normal tools usually put `claim` directly
540
- * on the write (`update({ id, data, claim })`) and let the SDK release it. Use
541
- * this namespace for multi-step handles and coordination screens.
542
- *
543
- * Same surface as the reactive {@link ClaimApi}, but every read is a server
544
- * round-trip, so `state`/`queue`/`reorder` are **awaited** here (the WebSocket
545
- * client resolves them synchronously from its local pool — which is what lets
546
- * `useAblo((ablo) => ablo.x.claim.state({ id }))` work inside a React render; a
547
- * stateless client has no pool to read, so the `Promise` is unavoidable).
548
- *
549
- * Mechanically DERIVED from `ClaimReadApi` via {@link AwaitedClaimMethod} so the
550
- * two transports can never drift: the ONLY difference is the uniform `Promise`
551
- * wrapper that statelessness forces. `claim({ id })` is identical (already async
552
- * on both); `state`/`queue`/`reorder`/`release` are the awaited form.
553
- */
554
- export type HttpClaimApi<T = Record<string, unknown>> = ((params: ClaimParams<T>) => Promise<HeldClaim<T>>) & {
555
- [K in keyof ClaimReadApi<T>]: AwaitedClaimMethod<ClaimReadApi<T>[K]>;
556
- };
557
- export interface ModelClient<T = Record<string, unknown>> {
558
- /**
559
- * Single-row read over HTTP. **Returns an envelope, not the bare row** — the
560
- * row is on `.data`, alongside the `.stamp` watermark (for stale-context
561
- * guards on the following write) and any active `.claims`. A stateless HTTP
562
- * client can't synthesize the watermark from a local snapshot, so the
563
- * envelope is load-bearing here (the WebSocket client's `retrieve` returns
564
- * `T | undefined` because it reads from the hydrated pool).
565
- *
566
- * ```ts
567
- * const deal = await ablo.deals.retrieve({ id });
568
- * deal.data?.recommendation; // ← the row is on .data
569
- * deal.stamp; // watermark — pass to the next write's readAt
570
- * ```
571
- */
572
- retrieve(params: ModelReadOptions & {
573
- readonly id: string;
574
- }): Promise<ModelRead<T>>;
575
- /**
576
- * Collection read over HTTP (server round-trip). Equality `where`, `orderBy`,
577
- * `limit`. Present on the stateless protocol client; the store-backed
578
- * `.model(name)` accessor omits it (use the typed `ablo.<model>.list` there).
579
- */
580
- list?(options?: ServerReadOptions<T>): Promise<T[]>;
581
- /**
582
- * Create a row and return it — the confirmed, authoritative server row (with
583
- * framework defaults like `createdAt`/`createdBy`), mirroring the WebSocket
584
- * client's `create`. A re-create of an existing caller-supplied id is
585
- * idempotent and returns the EXISTING row, not the input.
586
- */
587
- create(params: ModelMutationOptions & {
588
- readonly data: Record<string, unknown>;
589
- readonly id?: string | null;
590
- }): Promise<T>;
591
- update(params: ModelMutationOptions & {
592
- readonly id: string;
593
- readonly data: Record<string, unknown>;
594
- }): Promise<CommitReceipt>;
595
- /**
596
- * Update under contention with a function of the latest state —
597
- * `update(id, current => next)`, the `setState(prev => next)` of the data
598
- * layer. The SDK reads the freshest row, runs your updater, writes it as a
599
- * compare-and-swap against the row's watermark, and re-reads + re-runs on any
600
- * concurrent write. No claim, no identity, no conflict codes surface: the
601
- * write either lands or throws {@link AbloContentionError} once its reconcile
602
- * budget is spent. Return `null`/`undefined` from the updater to skip the
603
- * write (resolves to `undefined`).
604
- */
605
- update(id: string, updater: ModelUpdater<T>, options?: ContentionOptions): Promise<CommitReceipt | undefined>;
606
- delete(params: ModelMutationOptions & {
607
- readonly id: string;
608
- }): Promise<CommitReceipt>;
609
- /**
610
- * Durable lease + FIFO wait-line over HTTP — coordination without a socket.
611
- * Present on the stateless protocol client (`Ablo({ schema: null })` /
612
- * `createAbloHttpClient`); the store-backed `.model(name)` accessor omits it
613
- * (the typed `ablo.<model>.claim` proxy is the full reactive namespace there).
614
- */
615
- claim?: HttpClaimApi<T>;
616
- }
617
- /** A single data operation a scoped **agent** session may perform on a model. */
618
- export type SessionOperation = 'read' | 'create' | 'update' | 'delete';
619
- /** Mint params for an **end-user** session — full data authority within the
620
- * org (the Stripe `ephemeralKeys.create` / Supabase session shape). Mints an
621
- * `ek_` token. `user.id` is your end user's external IdP id (becomes the
622
- * session's `participantId`); Ablo does not model your users, so it's an
623
- * honest string at the trust boundary. */
624
- export interface CreateUserSessionParams {
625
- /** Your end user. `id` becomes the token's `participantId`. */
626
- user: {
627
- id: string;
628
- };
629
- /** Mint the session into THIS organization instead of the key's own org — the
630
- * Stripe Connect `Stripe-Account` pattern, for a platform serving many tenants
631
- * from one backend. Requires the `sk_` to carry the `ephemeral:mint-any-org`
632
- * scope; omit for the normal single-tenant case. */
633
- organizationId?: string;
634
- /** Sync groups this session may subscribe to — typed (`'default'` or
635
- * `<namespace>:<id>`; build with `syncGroup(kind, id)` from
636
- * `@abloatai/ablo/schema`). Omit for the server default:
637
- * `[org:<your org>, user:<user.id>]`. */
638
- syncGroups?: readonly SyncGroupInput[];
639
- /** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
640
- ttlSeconds?: number;
641
- /** Opaque identity blob echoed back to the client as `ablo.user`. */
642
- userMeta?: Record<string, unknown>;
643
- agent?: never;
644
- can?: never;
645
- }
646
- /** Mint params for a scoped **agent** session — mints a restricted `rk_` token
647
- * gated to exactly the operations named in `can`. `can` is typed off your
648
- * schema (no magic `'task.update'` strings): `{ Task: ['update'], Deck: ['read'] }`
649
- * — the SDK serializes each entry to the wire allowlist (`task.update`). */
650
- export interface CreateAgentSessionParams<S extends SchemaRecord> {
651
- /** Your agent. `id` becomes the token's `participantId`. */
652
- agent: {
653
- id: string;
654
- };
655
- /** Per-model operation allowlist, typed against the schema's model names. */
656
- can: {
657
- [M in keyof S & string]?: readonly SessionOperation[];
658
- };
659
- /** Sync groups this session may subscribe to — typed (`'default'` or
660
- * `<namespace>:<id>`; build with `syncGroup(kind, id)` from
661
- * `@abloatai/ablo/schema`). Omit for the server default: the org
662
- * anchor (`org:<your org>`) + the agent's own anchor. */
663
- syncGroups?: readonly SyncGroupInput[];
664
- /** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
665
- ttlSeconds?: number;
666
- /** Opaque identity blob echoed back to the client as `ablo.agent`. */
667
- userMeta?: Record<string, unknown>;
668
- user?: never;
669
- }
670
- /** Params for {@link Ablo.sessions}.create — a discriminated union: pass
671
- * `{ user }` for a full-authority end-user session (`ek_`) or `{ agent, can }`
672
- * for a scoped agent session (`rk_`). */
673
- export type CreateSessionParams<S extends SchemaRecord> = CreateUserSessionParams | CreateAgentSessionParams<S>;
674
- /** Params for {@link Ablo.agents}.create — a flattened agent descriptor (no
675
- * `{ agent }` discriminator: `agents.create` only ever mints an agent). Unlike
676
- * {@link CreateSessionParams} it resolves to a connected, scoped {@link Ablo}
677
- * client rather than a raw token. */
678
- export interface CreateAgentClientParams<S extends SchemaRecord> {
679
- /** Wire participant identity (`agent:<id>`) — what claim exclusion and the
680
- * FIFO queue gate on. OMIT to get a fresh `crypto.randomUUID()`: a distinct,
681
- * independent participant (the default, and what you want for concurrent
682
- * agents). Pass a STABLE string only when one logical agent must re-attach
683
- * to its own held claims across reconnects/restarts. */
684
- id?: string;
685
- /** Human-readable label for logs / attribution (carried in `userMeta.name`).
686
- * INDEPENDENT of `id`: two agents that share a `name` still receive distinct
687
- * ids and coordinate as SEPARATE participants — `name` never derives or
688
- * collapses identity. */
689
- name?: string;
690
- /** Per-model operation allowlist, typed against the schema's model names. */
691
- can: {
692
- [M in keyof S & string]?: readonly SessionOperation[];
693
- };
694
- /** Sync groups this agent may subscribe to — typed (`'default'` or
695
- * `<namespace>:<id>`). Omit for the server default (org anchor + the
696
- * agent's own anchor). */
697
- syncGroups?: readonly SyncGroupInput[];
698
- /** Token lifetime in seconds. Defaults to 900 (15m); the returned client
699
- * auto-re-mints before expiry, so a long-running agent never handles
700
- * rotation itself. */
701
- ttlSeconds?: number;
702
- /** Extra opaque identity blob echoed on the session scope. Merged with
703
- * `name` (the `name` param wins if you also set `userMeta.name`). */
704
- userMeta?: Record<string, unknown>;
705
- }
706
- /** A minted session token — the Stripe ephemeral-key / Supabase session
707
- * resource. `token` is the secret the holder presents as its bearer. */
708
- export interface AbloSession {
709
- object: 'session';
710
- /** Stable id of the minted credential (for revocation). */
711
- id: string;
712
- /** The short-lived session token — `ek_` for a `{ user }` session, `rk_`
713
- * for an `{ agent }` session. Hand this to the participant's runtime. */
714
- token: string;
715
- /** ISO-8601 expiry. */
716
- expiresAt: string;
717
- organizationId: string;
718
- scope: {
719
- organizationId: string;
720
- syncGroups: readonly string[];
721
- operations: readonly string[];
722
- participantKind: 'user' | 'agent' | 'system';
723
- participantId: string;
724
- };
725
- userMeta: Record<string, unknown>;
726
- }
31
+ import type { AbloOptions } from './options.js';
32
+ import type { AbloSession, CommitResource, CreateAgentClientParams, CreateSessionParams, ModelClient } from './resourceTypes.js';
33
+ export type { ApiKeySetter, AbloOptions, InternalAbloOptions } from './options.js';
34
+ export type { LocalCountOptions, LocalReadOptions, ModelListScope, ServerReadOptions, ModelRetrieveParams, ModelCreateParams, ModelUpdateParams, ModelDeleteParams, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, Claim, HeldClaim, ModelOperations, ModelOperationAction, CommitWait, ModelRead, IfClaimedPolicy, ClaimedOptions, ClaimWaitOptions, ModelReadOptions, ClaimCreateOptions, CommitOperationInput, CommitCreateOptions, CommitReceipt, CommitResource, ClaimResource, ModelMutationOptions, HttpClaimApi, ModelClient, SessionOperation, CreateUserSessionParams, CreateAgentSessionParams, CreateSessionParams, CreateAgentClientParams, AbloSession, } from './resourceTypes.js';
35
+ export { computeFKDepthPriority } from './schemaConfig.js';
36
+ import type { ModelOperations } from './createModelProxy.js';
727
37
  /** The typed sync engine client — one property per model in the schema */
728
38
  export type Ablo<S extends SchemaRecord> = {
729
39
  readonly [K in keyof S & string]: ModelOperations<InferModel<Schema<S>, K>, InferCreate<Schema<S>, K>>;
@@ -970,9 +280,7 @@ export type Ablo<S extends SchemaRecord> = {
970
280
  * await streamText({ messages, signal: snap.signal });
971
281
  * ```
972
282
  */
973
- snapshot<ModelName extends keyof S & string>(entities: {
974
- readonly [M in ModelName]: string | readonly string[];
975
- }): Snapshot<Schema<S>, ModelName>;
283
+ snapshot<ModelName extends keyof S & string>(entities: Readonly<Record<ModelName, string | readonly string[]>>): Snapshot<Schema<S>, ModelName>;
976
284
  /**
977
285
  * The internal BaseSyncedStore. Implements SyncStoreContract — pass to
978
286
  * SyncContext.Provider so the SDK's useModel/useModels/useMutations hooks
@@ -987,51 +295,6 @@ export type Ablo<S extends SchemaRecord> = {
987
295
  */
988
296
  readonly _ws: SyncWebSocket | null;
989
297
  };
990
- /**
991
- * Compute a create-priority map from schema `belongsTo` relations using
992
- * Tarjan's strongly-connected-components algorithm.
993
- *
994
- * The FK graph has an edge `child → parent` for every `belongsTo`. Tarjan
995
- * runs a single linear DFS that simultaneously (a) detects cycles by
996
- * grouping mutually-reachable nodes into SCCs and (b) emits those SCCs
997
- * in reverse topological order of the condensation graph. In this edge
998
- * convention a "sink" SCC has no outgoing edges — i.e. no parents — so
999
- * it is an *FK root* (`organizations`, `themes`, etc.). Tarjan emits
1000
- * roots first and leaves last, exactly the order in which rows must be
1001
- * inserted to satisfy FK constraints.
1002
- *
1003
- * Priorities are assigned by emit order: SCC #0 → 10, SCC #1 → 20, …
1004
- * Members of the same SCC share a priority, so insertion order wins the
1005
- * tiebreak inside a cycle (this matters for cyclic schemas like
1006
- * `slideDecks ↔ layouts`, where one direction is the user's chosen
1007
- * "soft" edge — only the consumer's mutator sequence knows which one).
1008
- *
1009
- * This algorithm is iteration-order-independent: starting the DFS from
1010
- * any node yields the same SCC partitioning, and SCCs always come out
1011
- * in valid topological order. The previous DFS-with-memoization
1012
- * heuristic broke under cycles by treating the back-edge as depth 0,
1013
- * which made priorities depend on which node the walk happened to
1014
- * enter the cycle at.
1015
- *
1016
- * Schema authors can mark one side of a cycle with
1017
- * `belongsTo(target, fk, { defer: true })`. Those edges are excluded
1018
- * from the dependency graph entirely, which deterministically breaks
1019
- * the cycle and turns the SCC into a chain — the marked child gets a
1020
- * strictly higher priority than its parent instead of being tied with
1021
- * it. Pair with a Postgres `DEFERRABLE INITIALLY DEFERRED` constraint
1022
- * if you want the database side of the cycle to also relax. See
1023
- * {@link BelongsToOptions.defer}.
1024
- *
1025
- * The returned map is keyed by {@link ModelDef.typename} (falling back
1026
- * to the schema key), because that is what `Model.getModelName()`
1027
- * returns at transaction time — keying by schema key would silently
1028
- * miss the lookup and every model would fall through to
1029
- * `defaultCreatePriority`.
1030
- *
1031
- * Reference: Tarjan, R. (1972), "Depth-first search and linear graph
1032
- * algorithms." Linear in V + E.
1033
- */
1034
- export declare function computeFKDepthPriority(schema: Schema): ReadonlyMap<string, number>;
1035
298
  /**
1036
299
  * Create a sync engine client in one call.
1037
300
  *
@@ -1042,6 +305,14 @@ export declare function computeFKDepthPriority(schema: Schema): ReadonlyMap<stri
1042
305
  * await sync.weatherReports.create({ location: 'Stockholm', status: 'pending' });
1043
306
  * ```
1044
307
  *
308
+ * In the browser (or any client that shouldn't hold a secret key), point
309
+ * `authEndpoint` at your session-mint route instead — the SDK fetches it, keeps the
310
+ * short-lived token fresh, and re-mints on expiry:
311
+ *
312
+ * ```ts
313
+ * const ablo = Ablo({ schema, authEndpoint: '/api/ablo-session' });
314
+ * ```
315
+ *
1045
316
  * Pass `transport: 'http'` for the stateless server-side client (agents,
1046
317
  * workers, serverless) — same `ablo.<model>` surface, no socket:
1047
318
  *
@@ -1091,8 +362,8 @@ export declare namespace Ablo {
1091
362
  type CapabilityRevocation = import('./ApiClient.js').CapabilityRevocation;
1092
363
  type CapabilityRotateOptions = import('./ApiClient.js').CapabilityRotateOptions;
1093
364
  type RotatedCapability = import('./ApiClient.js').RotatedCapability;
1094
- type IfClaimedPolicy = import('./Ablo.js').IfClaimedPolicy;
1095
- type ClaimedOptions = import('./Ablo.js').ClaimedOptions;
365
+ type IfClaimedPolicy = import('./resourceTypes.js').IfClaimedPolicy;
366
+ type ClaimedOptions = import('./resourceTypes.js').ClaimedOptions;
1096
367
  type ClaimTarget = _Streams.ClaimTarget;
1097
368
  type PresenceTarget = _Streams.PresenceTarget;
1098
369
  type TargetRange = _Streams.TargetRange;
@@ -1130,26 +401,26 @@ export declare namespace Ablo {
1130
401
  type Axis = _Policy.ConflictAxis;
1131
402
  }
1132
403
  namespace Commit {
1133
- type Wait = import('./Ablo.js').CommitWait;
1134
- type OperationAction = import('./Ablo.js').ModelOperationAction;
1135
- type OperationInput = import('./Ablo.js').CommitOperationInput;
1136
- type CreateOptions = import('./Ablo.js').CommitCreateOptions;
1137
- type Receipt = import('./Ablo.js').CommitReceipt;
1138
- type Client = import('./Ablo.js').CommitResource;
404
+ type Wait = import('./resourceTypes.js').CommitWait;
405
+ type OperationAction = import('./resourceTypes.js').ModelOperationAction;
406
+ type OperationInput = import('./resourceTypes.js').CommitOperationInput;
407
+ type CreateOptions = import('./resourceTypes.js').CommitCreateOptions;
408
+ type Receipt = import('./resourceTypes.js').CommitReceipt;
409
+ type Client = import('./resourceTypes.js').CommitResource;
1139
410
  }
1140
411
  namespace Claim {
1141
- type Handle = import('./Ablo.js').Claim;
1142
- type CreateOptions = import('./Ablo.js').ClaimCreateOptions;
1143
- type WaitOptions = import('./Ablo.js').ClaimWaitOptions;
1144
- type Client = import('./Ablo.js').ClaimResource;
412
+ type Handle = import('./resourceTypes.js').Claim;
413
+ type CreateOptions = import('./resourceTypes.js').ClaimCreateOptions;
414
+ type WaitOptions = import('./resourceTypes.js').ClaimWaitOptions;
415
+ type Client = import('./resourceTypes.js').ClaimResource;
1145
416
  }
1146
417
  namespace Model {
1147
- type Target = import('./Ablo.js').ModelTarget;
1148
- type Claim = import('./Ablo.js').ModelClaim;
1149
- type Read<T = Record<string, unknown>> = import('./Ablo.js').ModelRead<T>;
1150
- type Client<T = Record<string, unknown>> = import('./Ablo.js').ModelClient<T>;
1151
- type ReadOptions = import('./Ablo.js').ModelReadOptions;
1152
- type MutationOptions = import('./Ablo.js').ModelMutationOptions;
418
+ type Target = import('./resourceTypes.js').ModelTarget;
419
+ type Claim = import('./resourceTypes.js').ModelClaim;
420
+ type Read<T = Record<string, unknown>> = import('./resourceTypes.js').ModelRead<T>;
421
+ type Client<T = Record<string, unknown>> = import('./resourceTypes.js').ModelClient<T>;
422
+ type ReadOptions = import('./resourceTypes.js').ModelReadOptions;
423
+ type MutationOptions = import('./resourceTypes.js').ModelMutationOptions;
1153
424
  }
1154
425
  namespace Source {
1155
426
  type Operation = import('../source/index.js').SourceOperation;