@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.
- package/CHANGELOG.md +208 -0
- package/LICENSE +201 -0
- package/NOTICE +12 -0
- package/README.md +230 -0
- package/dist/BaseSyncedStore.d.ts +709 -0
- package/dist/BaseSyncedStore.js +1843 -0
- package/dist/Database.d.ts +344 -0
- package/dist/Database.js +1259 -0
- package/dist/LazyReferenceCollection.d.ts +181 -0
- package/dist/LazyReferenceCollection.js +460 -0
- package/dist/Model.d.ts +339 -0
- package/dist/Model.js +715 -0
- package/dist/ModelRegistry.d.ts +200 -0
- package/dist/ModelRegistry.js +535 -0
- package/dist/NetworkMonitor.d.ts +27 -0
- package/dist/NetworkMonitor.js +73 -0
- package/dist/ObjectPool.d.ts +202 -0
- package/dist/ObjectPool.js +1106 -0
- package/dist/SyncClient.d.ts +489 -0
- package/dist/SyncClient.js +1555 -0
- package/dist/SyncEngineContext.d.ts +46 -0
- package/dist/SyncEngineContext.js +74 -0
- package/dist/adapters/alwaysOnline.d.ts +16 -0
- package/dist/adapters/alwaysOnline.js +19 -0
- package/dist/adapters/inMemoryStorage.d.ts +30 -0
- package/dist/adapters/inMemoryStorage.js +94 -0
- package/dist/agent/Agent.d.ts +358 -0
- package/dist/agent/Agent.js +500 -0
- package/dist/agent/index.d.ts +115 -0
- package/dist/agent/index.js +128 -0
- package/dist/agent/session.d.ts +90 -0
- package/dist/agent/session.js +156 -0
- package/dist/agent/types.d.ts +73 -0
- package/dist/agent/types.js +10 -0
- package/dist/ai-sdk/coordination-context.d.ts +51 -0
- package/dist/ai-sdk/coordination-context.js +107 -0
- package/dist/ai-sdk/index.d.ts +68 -0
- package/dist/ai-sdk/index.js +68 -0
- package/dist/ai-sdk/intent-broadcast.d.ts +77 -0
- package/dist/ai-sdk/intent-broadcast.js +72 -0
- package/dist/ai-sdk/wrap.d.ts +67 -0
- package/dist/ai-sdk/wrap.js +45 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +9 -0
- package/dist/auth/index.d.ts +137 -0
- package/dist/auth/index.js +246 -0
- package/dist/client/Ablo.d.ts +835 -0
- package/dist/client/Ablo.js +1440 -0
- package/dist/client/ApiClient.d.ts +200 -0
- package/dist/client/ApiClient.js +659 -0
- package/dist/client/auth.d.ts +79 -0
- package/dist/client/auth.js +81 -0
- package/dist/client/createInternalComponents.d.ts +44 -0
- package/dist/client/createInternalComponents.js +88 -0
- package/dist/client/createModelProxy.d.ts +152 -0
- package/dist/client/createModelProxy.js +199 -0
- package/dist/client/identity.d.ts +63 -0
- package/dist/client/identity.js +156 -0
- package/dist/client/index.d.ts +36 -0
- package/dist/client/index.js +33 -0
- package/dist/client/persistence.d.ts +7 -0
- package/dist/client/persistence.js +11 -0
- package/dist/client/validateAbloOptions.d.ts +42 -0
- package/dist/client/validateAbloOptions.js +43 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.js +12 -0
- package/dist/context.d.ts +27 -0
- package/dist/context.js +58 -0
- package/dist/core/DatabaseManager.d.ts +108 -0
- package/dist/core/DatabaseManager.js +361 -0
- package/dist/core/QueryProcessor.d.ts +77 -0
- package/dist/core/QueryProcessor.js +262 -0
- package/dist/core/QueryView.d.ts +64 -0
- package/dist/core/QueryView.js +219 -0
- package/dist/core/StoreManager.d.ts +131 -0
- package/dist/core/StoreManager.js +334 -0
- package/dist/core/ViewRegistry.d.ts +20 -0
- package/dist/core/ViewRegistry.js +55 -0
- package/dist/core/index.d.ts +34 -0
- package/dist/core/index.js +59 -0
- package/dist/core/openIDBWithTimeout.d.ts +27 -0
- package/dist/core/openIDBWithTimeout.js +63 -0
- package/dist/core/query-utils.d.ts +37 -0
- package/dist/core/query-utils.js +60 -0
- package/dist/errors.d.ts +235 -0
- package/dist/errors.js +243 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +82 -0
- package/dist/interfaces/headless.d.ts +95 -0
- package/dist/interfaces/headless.js +41 -0
- package/dist/interfaces/index.d.ts +321 -0
- package/dist/interfaces/index.js +8 -0
- package/dist/mutators/RecordingTransaction.d.ts +36 -0
- package/dist/mutators/RecordingTransaction.js +216 -0
- package/dist/mutators/Transaction.d.ts +48 -0
- package/dist/mutators/Transaction.js +64 -0
- package/dist/mutators/UndoManager.d.ts +114 -0
- package/dist/mutators/UndoManager.js +143 -0
- package/dist/mutators/defineMutators.d.ts +55 -0
- package/dist/mutators/defineMutators.js +28 -0
- package/dist/policy/index.d.ts +19 -0
- package/dist/policy/index.js +18 -0
- package/dist/policy/types.d.ts +74 -0
- package/dist/policy/types.js +17 -0
- package/dist/principal.d.ts +44 -0
- package/dist/principal.js +49 -0
- package/dist/query/client.d.ts +43 -0
- package/dist/query/client.js +84 -0
- package/dist/query/index.d.ts +6 -0
- package/dist/query/index.js +5 -0
- package/dist/query/types.d.ts +143 -0
- package/dist/query/types.js +36 -0
- package/dist/react/AbloProvider.d.ts +205 -0
- package/dist/react/AbloProvider.js +398 -0
- package/dist/react/ClientSideSuspense.d.ts +36 -0
- package/dist/react/ClientSideSuspense.js +17 -0
- package/dist/react/DefaultFallback.d.ts +24 -0
- package/dist/react/DefaultFallback.js +43 -0
- package/dist/react/SyncGroupProvider.d.ts +19 -0
- package/dist/react/SyncGroupProvider.js +44 -0
- package/dist/react/context.d.ts +161 -0
- package/dist/react/context.js +35 -0
- package/dist/react/index.d.ts +64 -0
- package/dist/react/index.js +73 -0
- package/dist/react/internalContext.d.ts +35 -0
- package/dist/react/internalContext.js +3 -0
- package/dist/react/useAblo.d.ts +72 -0
- package/dist/react/useAblo.js +63 -0
- package/dist/react/useCurrentUserId.d.ts +21 -0
- package/dist/react/useCurrentUserId.js +33 -0
- package/dist/react/useErrorListener.d.ts +20 -0
- package/dist/react/useErrorListener.js +39 -0
- package/dist/react/useIntent.d.ts +29 -0
- package/dist/react/useIntent.js +42 -0
- package/dist/react/useMutate.d.ts +83 -0
- package/dist/react/useMutate.js +122 -0
- package/dist/react/useMutationFailureListener.d.ts +26 -0
- package/dist/react/useMutationFailureListener.js +38 -0
- package/dist/react/useMutators.d.ts +56 -0
- package/dist/react/useMutators.js +66 -0
- package/dist/react/usePresence.d.ts +32 -0
- package/dist/react/usePresence.js +41 -0
- package/dist/react/useQuery.d.ts +123 -0
- package/dist/react/useQuery.js +145 -0
- package/dist/react/useReactive.d.ts +35 -0
- package/dist/react/useReactive.js +111 -0
- package/dist/react/useReader.d.ts +69 -0
- package/dist/react/useReader.js +73 -0
- package/dist/react/useSyncStatus.d.ts +61 -0
- package/dist/react/useSyncStatus.js +76 -0
- package/dist/react/useUndoScope.d.ts +36 -0
- package/dist/react/useUndoScope.js +73 -0
- package/dist/realtime/index.d.ts +10 -0
- package/dist/realtime/index.js +9 -0
- package/dist/schema/field.d.ts +134 -0
- package/dist/schema/field.js +264 -0
- package/dist/schema/index.d.ts +29 -0
- package/dist/schema/index.js +38 -0
- package/dist/schema/model.d.ts +326 -0
- package/dist/schema/model.js +89 -0
- package/dist/schema/queries.d.ts +203 -0
- package/dist/schema/queries.js +145 -0
- package/dist/schema/relation.d.ts +172 -0
- package/dist/schema/relation.js +104 -0
- package/dist/schema/schema.d.ts +259 -0
- package/dist/schema/schema.js +188 -0
- package/dist/schema/sugar.d.ts +129 -0
- package/dist/schema/sugar.js +94 -0
- package/dist/source/index.d.ts +423 -0
- package/dist/source/index.js +320 -0
- package/dist/source/pushQueue.d.ts +112 -0
- package/dist/source/pushQueue.js +249 -0
- package/dist/stores/ObjectStore.d.ts +103 -0
- package/dist/stores/ObjectStore.js +371 -0
- package/dist/stores/ObjectStoreContract.d.ts +39 -0
- package/dist/stores/ObjectStoreContract.js +1 -0
- package/dist/stores/SyncActionStore.d.ts +101 -0
- package/dist/stores/SyncActionStore.js +481 -0
- package/dist/sync/BootstrapHelper.d.ts +127 -0
- package/dist/sync/BootstrapHelper.js +434 -0
- package/dist/sync/ConnectionManager.d.ts +136 -0
- package/dist/sync/ConnectionManager.js +465 -0
- package/dist/sync/HydrationCoordinator.d.ts +137 -0
- package/dist/sync/HydrationCoordinator.js +468 -0
- package/dist/sync/NetworkProbe.d.ts +43 -0
- package/dist/sync/NetworkProbe.js +113 -0
- package/dist/sync/OfflineFlush.d.ts +9 -0
- package/dist/sync/OfflineFlush.js +22 -0
- package/dist/sync/OfflineTransactionStore.d.ts +37 -0
- package/dist/sync/OfflineTransactionStore.js +263 -0
- package/dist/sync/SyncWebSocket.d.ts +663 -0
- package/dist/sync/SyncWebSocket.js +1336 -0
- package/dist/sync/createIntentStream.d.ts +33 -0
- package/dist/sync/createIntentStream.js +243 -0
- package/dist/sync/createPresenceStream.d.ts +46 -0
- package/dist/sync/createPresenceStream.js +192 -0
- package/dist/sync/createSnapshot.d.ts +33 -0
- package/dist/sync/createSnapshot.js +124 -0
- package/dist/sync/participants.d.ts +114 -0
- package/dist/sync/participants.js +336 -0
- package/dist/sync/schemas.d.ts +79 -0
- package/dist/sync/schemas.js +78 -0
- package/dist/testing/fixtures/bootstrap.d.ts +45 -0
- package/dist/testing/fixtures/bootstrap.js +53 -0
- package/dist/testing/fixtures/deltas.d.ts +86 -0
- package/dist/testing/fixtures/deltas.js +139 -0
- package/dist/testing/fixtures/models.d.ts +82 -0
- package/dist/testing/fixtures/models.js +270 -0
- package/dist/testing/helpers/react-wrapper.d.ts +66 -0
- package/dist/testing/helpers/react-wrapper.js +64 -0
- package/dist/testing/helpers/sync-engine-harness.d.ts +55 -0
- package/dist/testing/helpers/sync-engine-harness.js +70 -0
- package/dist/testing/helpers/wait.d.ts +25 -0
- package/dist/testing/helpers/wait.js +44 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.js +32 -0
- package/dist/testing/mocks/MockMutationExecutor.d.ts +65 -0
- package/dist/testing/mocks/MockMutationExecutor.js +139 -0
- package/dist/testing/mocks/MockNetworkMonitor.d.ts +20 -0
- package/dist/testing/mocks/MockNetworkMonitor.js +46 -0
- package/dist/testing/mocks/MockSyncContext.d.ts +64 -0
- package/dist/testing/mocks/MockSyncContext.js +100 -0
- package/dist/testing/mocks/MockSyncStore.d.ts +88 -0
- package/dist/testing/mocks/MockSyncStore.js +171 -0
- package/dist/testing/mocks/MockWebSocket.d.ts +66 -0
- package/dist/testing/mocks/MockWebSocket.js +117 -0
- package/dist/transactions/OptimisticEchoTracker.d.ts +82 -0
- package/dist/transactions/OptimisticEchoTracker.js +104 -0
- package/dist/transactions/TransactionQueue.d.ts +499 -0
- package/dist/transactions/TransactionQueue.js +1895 -0
- package/dist/transactions/index.d.ts +16 -0
- package/dist/transactions/index.js +7 -0
- package/dist/transactions/mutation-error-handler.d.ts +5 -0
- package/dist/transactions/mutation-error-handler.js +39 -0
- package/dist/types/global.d.ts +107 -0
- package/dist/types/global.js +38 -0
- package/dist/types/index.d.ts +241 -0
- package/dist/types/index.js +70 -0
- package/dist/types/streams.d.ts +495 -0
- package/dist/types/streams.js +11 -0
- package/dist/utils/asyncIterator.d.ts +41 -0
- package/dist/utils/asyncIterator.js +142 -0
- package/dist/utils/duration.d.ts +28 -0
- package/dist/utils/duration.js +47 -0
- package/dist/utils/mobx-setup.d.ts +42 -0
- package/dist/utils/mobx-setup.js +381 -0
- package/docs/api-keys.md +24 -0
- package/docs/api.md +230 -0
- package/docs/audit.md +81 -0
- package/docs/capabilities.md +163 -0
- package/docs/client-behavior.md +202 -0
- package/docs/data-sources.md +214 -0
- package/docs/examples/agent-human.md +84 -0
- package/docs/examples/ai-sdk-tool.md +92 -0
- package/docs/examples/existing-python-backend.md +249 -0
- package/docs/examples/nextjs.md +88 -0
- package/docs/examples/server-agent.md +86 -0
- package/docs/guarantees.md +148 -0
- package/docs/index.md +97 -0
- package/docs/integration-guide.md +493 -0
- package/docs/interaction-model.md +140 -0
- package/docs/mcp/claude-code.md +43 -0
- package/docs/mcp/cursor.md +53 -0
- package/docs/mcp/windsurf.md +46 -0
- package/docs/mcp.md +59 -0
- package/docs/quickstart.md +152 -0
- package/docs/react.md +115 -0
- package/docs/roadmap.md +45 -0
- package/examples/README.md +54 -0
- package/examples/data-source/README.md +102 -0
- package/examples/data-source/ablo-driver.ts +89 -0
- package/examples/data-source/customer-server.ts +208 -0
- package/examples/data-source/run.ts +101 -0
- package/examples/data-source/schema.ts +25 -0
- package/examples/quickstart.ts +54 -0
- package/examples/tsconfig.json +16 -0
- package/llms.txt +143 -0
- package/package.json +147 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ablo — The one-liner consumer API.
|
|
3
|
+
*
|
|
4
|
+
* Hides all internal wiring (ObjectPool, Database, SyncClient, WebSocket,
|
|
5
|
+
* bootstrap, offline queue, DI adapters) behind a single function call.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { Ablo } from '@ablo/sync-engine/client';
|
|
9
|
+
* import { schema } from './schema';
|
|
10
|
+
*
|
|
11
|
+
* const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
12
|
+
*
|
|
13
|
+
* const tasks = sync.tasks.list({ where: { status: 'todo' } });
|
|
14
|
+
* await sync.tasks.create({ title: 'Fix bug' });
|
|
15
|
+
* await sync.tasks.update(taskId, { status: 'done' });
|
|
16
|
+
* await sync.tasks.delete(taskId);
|
|
17
|
+
*/
|
|
18
|
+
import type { Schema, SchemaRecord, InferModel, InferCreate } from '../schema/schema.js';
|
|
19
|
+
import type { SyncEngineConfig, SyncLogger, MutationExecutor, MutationDispatcher, SyncObservabilityProvider, SyncAnalytics, SessionErrorDetector, OnlineStatusProvider } from '../interfaces/index.js';
|
|
20
|
+
import { ObjectPool } from '../ObjectPool.js';
|
|
21
|
+
import type { SyncStoreContract } from '../react/context.js';
|
|
22
|
+
import type { SyncWebSocket } from '../sync/SyncWebSocket.js';
|
|
23
|
+
import { type SyncStatus } from '../BaseSyncedStore.js';
|
|
24
|
+
import type { IntentStream, PresenceStream, Snapshot } from '../types/streams.js';
|
|
25
|
+
import type { ParticipantManager } from '../sync/participants.js';
|
|
26
|
+
import type { ActiveIntent, Duration, TargetRange } from '../types/streams.js';
|
|
27
|
+
import { type AbloApi, type AbloApiClientOptions, type AbloApiIntents } from './ApiClient.js';
|
|
28
|
+
/**
|
|
29
|
+
* Handle returned by `engine.beginTurn()`. While alive, every commit
|
|
30
|
+
* automatically carries this turn's id on the wire. Call `close(stats?)`
|
|
31
|
+
* when the turn finishes, or `dispose()` to abandon without recording
|
|
32
|
+
* usage. Idempotent.
|
|
33
|
+
*/
|
|
34
|
+
export interface Turn {
|
|
35
|
+
readonly turnId: string;
|
|
36
|
+
close(stats?: {
|
|
37
|
+
readonly costInputTokens?: number;
|
|
38
|
+
readonly costOutputTokens?: number;
|
|
39
|
+
readonly costComputeMs?: number;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
dispose(): void;
|
|
42
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Async function that resolves an apiKey at request time. Use for
|
|
46
|
+
* credential rotation — rotate from a vault, refresh from session
|
|
47
|
+
* storage, or pull from a Better Auth session. Mirrors Anthropic's
|
|
48
|
+
* `ApiKeySetter` exactly so any rotation pattern that works with
|
|
49
|
+
* `@anthropic-ai/sdk` works here.
|
|
50
|
+
*
|
|
51
|
+
* Re-exported from `./auth` so existing import paths (`@ablo/sync-engine`)
|
|
52
|
+
* keep resolving; the canonical definition lives there alongside the
|
|
53
|
+
* resolvers that consume it.
|
|
54
|
+
*/
|
|
55
|
+
export type { ApiKeySetter } from './auth.js';
|
|
56
|
+
import type { ApiKeySetter } from './auth.js';
|
|
57
|
+
import { type AbloPersistence } from './persistence.js';
|
|
58
|
+
export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
59
|
+
/**
|
|
60
|
+
* API key used for authentication.
|
|
61
|
+
*
|
|
62
|
+
* Accepts a static string (`sk_live_...`) or an async function that
|
|
63
|
+
* resolves to one. Defaults to `process.env['ABLO_API_KEY']`.
|
|
64
|
+
*/
|
|
65
|
+
apiKey?: string | ApiKeySetter | null | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Bearer auth token. Hosted-cloud consumers pass `apiKey`; self-hosted
|
|
68
|
+
* deployments may pass a bearer token minted by their own auth layer.
|
|
69
|
+
*/
|
|
70
|
+
authToken?: string | null | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Override the Ablo API base URL. Defaults to hosted production and reads
|
|
73
|
+
* `process.env['ABLO_BASE_URL']` if unset.
|
|
74
|
+
*/
|
|
75
|
+
baseURL?: string | null | undefined;
|
|
76
|
+
/** Per-request timeout in milliseconds. */
|
|
77
|
+
timeout?: number | undefined;
|
|
78
|
+
/** Number of retries for transient failures. */
|
|
79
|
+
maxRetries?: number | undefined;
|
|
80
|
+
/** Custom fetch implementation for tests, proxies, or non-standard runtimes. */
|
|
81
|
+
fetch?: typeof fetch | undefined;
|
|
82
|
+
/** Default headers sent with every API request. */
|
|
83
|
+
defaultHeaders?: Record<string, string | null | undefined> | undefined;
|
|
84
|
+
/** Default query parameters sent with every API request. */
|
|
85
|
+
defaultQuery?: Record<string, string | undefined> | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* Client-side use is disabled by default because private API keys should
|
|
88
|
+
* not ship to browsers. Set this only when using a publishable/browser-safe
|
|
89
|
+
* key or a controlled server proxy.
|
|
90
|
+
*/
|
|
91
|
+
dangerouslyAllowBrowser?: boolean | undefined;
|
|
92
|
+
/**
|
|
93
|
+
* TypeScript schema defined with `defineSchema()`. This enables typed
|
|
94
|
+
* resources such as `ablo.tasks.update(...)`.
|
|
95
|
+
*/
|
|
96
|
+
schema: Schema<S>;
|
|
97
|
+
/**
|
|
98
|
+
* Local persistence mode. Defaults to `volatile`. Pass `indexeddb` only
|
|
99
|
+
* when you want offline queueing and a reload-surviving browser cache.
|
|
100
|
+
*/
|
|
101
|
+
persistence?: AbloPersistence;
|
|
102
|
+
}
|
|
103
|
+
export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
104
|
+
/**
|
|
105
|
+
* API key used for authentication.
|
|
106
|
+
*
|
|
107
|
+
* Accepts a static string (`sk_live_...`) or an async function that
|
|
108
|
+
* resolves to one. Defaults to `process.env['ABLO_API_KEY']`.
|
|
109
|
+
*
|
|
110
|
+
* When a function is provided, it's invoked before each request so
|
|
111
|
+
* you can rotate or refresh credentials at runtime. The function
|
|
112
|
+
* must return a non-empty string; otherwise an `AbloAuthenticationError`
|
|
113
|
+
* is thrown. If the function throws, the error is wrapped with the
|
|
114
|
+
* original available as `cause`.
|
|
115
|
+
*
|
|
116
|
+
* Mirrors Anthropic / OpenAI / Stripe SDK shape exactly.
|
|
117
|
+
*/
|
|
118
|
+
apiKey?: string | ApiKeySetter | null | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Bearer auth token. Sent as `Authorization: Bearer <token>` on
|
|
121
|
+
* every request. Defaults to `process.env['ABLO_AUTH_TOKEN']`.
|
|
122
|
+
*
|
|
123
|
+
* Use this for self-hosted deployments where your auth layer mints
|
|
124
|
+
* cap tokens directly. Hosted-cloud consumers pass `apiKey` instead;
|
|
125
|
+
* the server handles cap-mint internally.
|
|
126
|
+
*/
|
|
127
|
+
authToken?: string | null | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* Override the default base URL. Defaults to
|
|
130
|
+
* `wss://mesh.ablo.finance` for hosted production; pass an explicit
|
|
131
|
+
* URL for self-hosted or staging (e.g. `wss://mesh-staging.ablo.finance`).
|
|
132
|
+
* Reads `process.env['ABLO_BASE_URL']` if unset.
|
|
133
|
+
*/
|
|
134
|
+
baseURL?: string | null | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Maximum amount of time (ms) the client waits for a response
|
|
137
|
+
* before timing out a single request. Defaults to 10 minutes
|
|
138
|
+
* (600_000ms). Retried requests can wait longer in worst case.
|
|
139
|
+
*/
|
|
140
|
+
timeout?: number | undefined;
|
|
141
|
+
/**
|
|
142
|
+
* Maximum number of times the client will retry a request on
|
|
143
|
+
* transient failure (5xx / 429 / network error). Defaults to 2.
|
|
144
|
+
* Honors `Retry-After` and `retry-after-ms` response headers.
|
|
145
|
+
*/
|
|
146
|
+
maxRetries?: number | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* Custom `fetch` implementation. Defaults to `globalThis.fetch`.
|
|
149
|
+
* Override for testing, custom transports, or runtime shims.
|
|
150
|
+
*/
|
|
151
|
+
fetch?: typeof fetch | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Default headers to include with every request to the API.
|
|
154
|
+
* Removed per-request by setting the header to `null` in request
|
|
155
|
+
* options.
|
|
156
|
+
*/
|
|
157
|
+
defaultHeaders?: Record<string, string | null | undefined> | undefined;
|
|
158
|
+
/**
|
|
159
|
+
* Default query parameters to include with every request.
|
|
160
|
+
* Removed per-request by setting the param to `undefined`.
|
|
161
|
+
*/
|
|
162
|
+
defaultQuery?: Record<string, string | undefined> | undefined;
|
|
163
|
+
/**
|
|
164
|
+
* Client-side use of this SDK is disabled by default — your apiKey
|
|
165
|
+
* would ship to every visitor's network tab. Only set this to
|
|
166
|
+
* `true` if you've understood the risk and have appropriate
|
|
167
|
+
* mitigations (a publishable key, a server-side proxy, etc).
|
|
168
|
+
*/
|
|
169
|
+
dangerouslyAllowBrowser?: boolean | undefined;
|
|
170
|
+
/**
|
|
171
|
+
* TypeScript schema defined with `defineSchema()`.
|
|
172
|
+
*
|
|
173
|
+
* The root `Ablo(...)` client is schema-first so consumers get typed
|
|
174
|
+
* model resources such as `ablo.tasks.update(...)`. Omit `schema`
|
|
175
|
+
* only for the advanced Resource / Intent / Commit client.
|
|
176
|
+
*/
|
|
177
|
+
schema: Schema<S>;
|
|
178
|
+
/**
|
|
179
|
+
* @deprecated Server derives participant kind from the apiKey's
|
|
180
|
+
* scope. Pass apiKey only; this option will be removed once the
|
|
181
|
+
* server-internal cap-mint flow lands.
|
|
182
|
+
*/
|
|
183
|
+
kind?: 'user' | 'agent' | 'system';
|
|
184
|
+
/**
|
|
185
|
+
* @deprecated Server derives user identity from the apiKey's
|
|
186
|
+
* scope (or from `Ablo-Acting-User` request header for B2B2C).
|
|
187
|
+
* Removed once Phase 3 ships.
|
|
188
|
+
*/
|
|
189
|
+
user?: {
|
|
190
|
+
id: string;
|
|
191
|
+
teamIds?: string[];
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* @deprecated Server derives agent identity from the apiKey's
|
|
195
|
+
* scope. Removed once Phase 3 ships.
|
|
196
|
+
*/
|
|
197
|
+
agentId?: string;
|
|
198
|
+
/**
|
|
199
|
+
* @deprecated Cap-mint moves server-internal in Phase 3. Pass
|
|
200
|
+
* `apiKey` only; the server handles capability issuance.
|
|
201
|
+
*/
|
|
202
|
+
capabilityToken?: string;
|
|
203
|
+
/** Custom logger (default: console) */
|
|
204
|
+
logger?: SyncLogger;
|
|
205
|
+
/** ObjectPool size limit (default: 10000) */
|
|
206
|
+
maxPoolSize?: number;
|
|
207
|
+
/**
|
|
208
|
+
* Local persistence mode. Defaults to `volatile` so Ablo behaves like a
|
|
209
|
+
* point solution for shared state instead of silently bolting IndexedDB
|
|
210
|
+
* durability onto every browser consumer.
|
|
211
|
+
*
|
|
212
|
+
* Pass `persistence: 'indexeddb'` only when you want offline queueing
|
|
213
|
+
* and a reload-surviving local cache in a browser.
|
|
214
|
+
*/
|
|
215
|
+
persistence?: AbloPersistence;
|
|
216
|
+
/** @deprecated Use `persistence: 'indexeddb'` for durable browser storage. */
|
|
217
|
+
offline?: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* @deprecated Internal/testing escape hatch. Use `persistence` in
|
|
220
|
+
* production code. `true` maps to `volatile`; `false` maps to
|
|
221
|
+
* `indexeddb` in browsers.
|
|
222
|
+
*/
|
|
223
|
+
inMemory?: boolean;
|
|
224
|
+
/**
|
|
225
|
+
* If true, initialization starts immediately in the background so
|
|
226
|
+
* `sync.tasks.findMany()` works after `await sync.ready()`.
|
|
227
|
+
*
|
|
228
|
+
* If false (default), the consumer MUST call `await sync.ready()` before
|
|
229
|
+
* using the engine — any query before that returns empty results.
|
|
230
|
+
*
|
|
231
|
+
* Default: false (explicit is better — prevents silent init failures).
|
|
232
|
+
*/
|
|
233
|
+
autoStart?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* How aggressively this client should pull baseline state at
|
|
236
|
+
* startup.
|
|
237
|
+
*
|
|
238
|
+
* - `'full'`: pull every delta in the configured sync groups before
|
|
239
|
+
* `ready()` resolves. Default for `kind: 'user'`.
|
|
240
|
+
* - `'none'`: open the WS and process live deltas only — no baseline
|
|
241
|
+
* fetch. Reads round-trip via `resource.retrieve()`; subscriptions
|
|
242
|
+
* populate the pool lazily via covering deltas. Default for
|
|
243
|
+
* `kind: 'agent'` because agent-worker / routine runners don't
|
|
244
|
+
* need (or want) a local replica of the org's tenant plane.
|
|
245
|
+
*/
|
|
246
|
+
bootstrapMode?: 'full' | 'none';
|
|
247
|
+
/**
|
|
248
|
+
* Custom observability provider (Sentry, Honeycomb, OTel, etc.).
|
|
249
|
+
* Default: a noop implementation that drops all breadcrumbs and spans.
|
|
250
|
+
*/
|
|
251
|
+
observability?: SyncObservabilityProvider;
|
|
252
|
+
/**
|
|
253
|
+
* Custom analytics provider (PostHog, Amplitude, Segment, etc.).
|
|
254
|
+
* Default: a noop implementation that drops all events.
|
|
255
|
+
*/
|
|
256
|
+
analytics?: SyncAnalytics;
|
|
257
|
+
/**
|
|
258
|
+
* Detect whether an error from a mutation/bootstrap response means the
|
|
259
|
+
* user's session has expired. Used to surface re-auth prompts. Default:
|
|
260
|
+
* heuristic that matches `401 Unauthorized` and a few common error shapes.
|
|
261
|
+
*/
|
|
262
|
+
sessionErrorDetector?: SessionErrorDetector;
|
|
263
|
+
/**
|
|
264
|
+
* Detect whether the browser is currently online. Default: reads
|
|
265
|
+
* `navigator.onLine` and listens to the `online`/`offline` events.
|
|
266
|
+
*/
|
|
267
|
+
onlineStatus?: OnlineStatusProvider;
|
|
268
|
+
/**
|
|
269
|
+
* Replace the built-in `MutationExecutor` (which posts a hardcoded
|
|
270
|
+
* `commit` method against `${url}/graphql`) with one that uses your own
|
|
271
|
+
* GraphQL client, auth headers, retry policy, and observability hooks.
|
|
272
|
+
*
|
|
273
|
+
* Default: a fetch-based executor that targets `${url}/graphql` with
|
|
274
|
+
* `credentials: 'include'` (cookie auth) when no `apiKey` is set.
|
|
275
|
+
*/
|
|
276
|
+
mutationExecutor?: MutationExecutor;
|
|
277
|
+
/**
|
|
278
|
+
* Replace the built-in `MutationDispatcher` (used by the offline queue
|
|
279
|
+
* to replay mutations on reconnect). If you override `mutationExecutor`
|
|
280
|
+
* you almost always want to override this too so the two paths share
|
|
281
|
+
* the same auth/retry behavior.
|
|
282
|
+
*
|
|
283
|
+
* Default: a thin dispatcher that routes to the built-in executor.
|
|
284
|
+
*/
|
|
285
|
+
mutationDispatcher?: MutationDispatcher;
|
|
286
|
+
/**
|
|
287
|
+
* Partial overrides for the auto-derived `SyncEngineConfig`. Merged on
|
|
288
|
+
* top of `deriveConfigFromSchema(schema)`. Use this when you need
|
|
289
|
+
* specific `modelCreatePriority`, `batchableModels`, or
|
|
290
|
+
* `essentialFields` settings that the schema cannot express.
|
|
291
|
+
*/
|
|
292
|
+
configOverrides?: Partial<SyncEngineConfig>;
|
|
293
|
+
/**
|
|
294
|
+
* @deprecated Server derives sync groups from the apiKey's scope.
|
|
295
|
+
* Required today as a runtime holdover; removed once Phase 3 ships.
|
|
296
|
+
*/
|
|
297
|
+
syncGroups?: string[];
|
|
298
|
+
/**
|
|
299
|
+
* Override the bootstrap endpoint base URL. Use this when your sync
|
|
300
|
+
* server's HTTP API lives on a different host than the WebSocket URL.
|
|
301
|
+
*
|
|
302
|
+
* Must include the `/api` prefix — `BootstrapHelper` appends
|
|
303
|
+
* `/sync/bootstrap` directly. Example:
|
|
304
|
+
* `'http://api.example.com/api'` → `http://api.example.com/api/sync/bootstrap`.
|
|
305
|
+
*
|
|
306
|
+
* Default: `${url.replace(/^ws/, 'http')}/api`.
|
|
307
|
+
*/
|
|
308
|
+
bootstrapBaseUrl?: string;
|
|
309
|
+
/**
|
|
310
|
+
* Ablo-owned account scope. Required for Branch 3 identity resolution
|
|
311
|
+
* in `identity.ts` — without it the SDK falls through to the
|
|
312
|
+
* `/api/identity` HTTP-derived path (Branch 2).
|
|
313
|
+
*/
|
|
314
|
+
organizationId?: string;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Operations available on each model in the sync engine.
|
|
318
|
+
*
|
|
319
|
+
* Naming aligns with Stripe / OpenAI / Anthropic conventions:
|
|
320
|
+
* `retrieve(id)` — single entity by id (sync, from local pool)
|
|
321
|
+
* `list({where})` — collection with filter (sync, from local pool)
|
|
322
|
+
* `count({where})` — count (sync, from local pool)
|
|
323
|
+
* `load({where})` — async hydrate through pool → IDB → network
|
|
324
|
+
* `create / update / delete` — optimistic writes
|
|
325
|
+
*
|
|
326
|
+
* The old verb set (`findById`, `findMany`, `findFirst`) is kept as
|
|
327
|
+
* deprecated aliases for one release cycle so consumers can migrate
|
|
328
|
+
* without a flag day.
|
|
329
|
+
*/
|
|
330
|
+
export type { ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelEditHandle, ModelEditOptions, ModelOperations, } from './createModelProxy.js';
|
|
331
|
+
import type { ModelOperations } from './createModelProxy.js';
|
|
332
|
+
export type ResourceOperationAction = 'create' | 'update' | 'delete' | 'archive' | 'unarchive';
|
|
333
|
+
export type CommitWait = 'queued' | 'confirmed';
|
|
334
|
+
export interface ResourceTarget {
|
|
335
|
+
readonly resource: string;
|
|
336
|
+
readonly id: string;
|
|
337
|
+
readonly path?: string;
|
|
338
|
+
readonly range?: TargetRange;
|
|
339
|
+
readonly field?: string;
|
|
340
|
+
readonly meta?: Record<string, unknown>;
|
|
341
|
+
}
|
|
342
|
+
export interface ResourceIntent {
|
|
343
|
+
readonly id: string;
|
|
344
|
+
readonly actor: string;
|
|
345
|
+
readonly participantKind: ActiveIntent['participantKind'];
|
|
346
|
+
readonly action: string;
|
|
347
|
+
readonly field?: string;
|
|
348
|
+
readonly expiresAt: string;
|
|
349
|
+
readonly target: ResourceTarget;
|
|
350
|
+
}
|
|
351
|
+
export interface ResourceRead<T = Record<string, unknown>> {
|
|
352
|
+
readonly data: T;
|
|
353
|
+
readonly stamp: number;
|
|
354
|
+
readonly intents: readonly ResourceIntent[];
|
|
355
|
+
}
|
|
356
|
+
export type BusyPolicy = 'return' | 'wait' | 'fail';
|
|
357
|
+
export interface BusyOptions {
|
|
358
|
+
/**
|
|
359
|
+
* What to do when another participant has an active intent on the
|
|
360
|
+
* target. `return` includes the intents in the response, `wait`
|
|
361
|
+
* resolves after they clear, and `fail` throws `AbloBusyError`.
|
|
362
|
+
*/
|
|
363
|
+
readonly ifBusy?: BusyPolicy;
|
|
364
|
+
/** Max time to wait for peer intents to clear, in milliseconds. */
|
|
365
|
+
readonly busyTimeout?: number;
|
|
366
|
+
/** HTTP API polling interval while waiting. WebSocket clients ignore it. */
|
|
367
|
+
readonly busyPollInterval?: number;
|
|
368
|
+
}
|
|
369
|
+
export interface IntentWaitOptions {
|
|
370
|
+
readonly timeout?: number;
|
|
371
|
+
readonly pollInterval?: number;
|
|
372
|
+
readonly signal?: AbortSignal;
|
|
373
|
+
}
|
|
374
|
+
export interface ResourceReadOptions extends BusyOptions {
|
|
375
|
+
}
|
|
376
|
+
export interface IntentCreateOptions {
|
|
377
|
+
readonly target: ResourceTarget;
|
|
378
|
+
readonly action: string;
|
|
379
|
+
readonly ttl?: Duration;
|
|
380
|
+
}
|
|
381
|
+
export interface IntentHandle extends AsyncDisposable {
|
|
382
|
+
readonly id: string;
|
|
383
|
+
release(): Promise<void>;
|
|
384
|
+
revoke(): void;
|
|
385
|
+
}
|
|
386
|
+
export interface CommitOperationInput {
|
|
387
|
+
readonly action: ResourceOperationAction;
|
|
388
|
+
readonly resource?: string;
|
|
389
|
+
readonly target?: ResourceTarget;
|
|
390
|
+
readonly id?: string | null;
|
|
391
|
+
readonly data?: Record<string, unknown> | null;
|
|
392
|
+
readonly transactionId?: string | null;
|
|
393
|
+
readonly readAt?: number | null;
|
|
394
|
+
readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
|
|
395
|
+
}
|
|
396
|
+
export interface CommitCreateOptions {
|
|
397
|
+
readonly intent?: string | {
|
|
398
|
+
readonly id: string;
|
|
399
|
+
} | null;
|
|
400
|
+
readonly idempotencyKey?: string | null;
|
|
401
|
+
readonly readAt?: number | null;
|
|
402
|
+
readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
|
|
403
|
+
readonly operation?: CommitOperationInput;
|
|
404
|
+
readonly operations?: readonly CommitOperationInput[];
|
|
405
|
+
readonly wait?: CommitWait;
|
|
406
|
+
readonly timeout?: number;
|
|
407
|
+
}
|
|
408
|
+
export interface CommitReceipt {
|
|
409
|
+
readonly id: string;
|
|
410
|
+
readonly status: CommitWait;
|
|
411
|
+
readonly lastSyncId?: number;
|
|
412
|
+
}
|
|
413
|
+
export interface CommitResource {
|
|
414
|
+
create(options: CommitCreateOptions): Promise<CommitReceipt>;
|
|
415
|
+
}
|
|
416
|
+
export interface IntentResource extends IntentStream {
|
|
417
|
+
create(options: IntentCreateOptions): Promise<IntentHandle>;
|
|
418
|
+
list(target?: Partial<ResourceTarget>): readonly ResourceIntent[];
|
|
419
|
+
waitFor(target: Partial<ResourceTarget>, options?: IntentWaitOptions): Promise<void>;
|
|
420
|
+
}
|
|
421
|
+
export interface ResourceMutationOptions extends BusyOptions {
|
|
422
|
+
readonly intent?: string | {
|
|
423
|
+
readonly id: string;
|
|
424
|
+
} | null;
|
|
425
|
+
readonly idempotencyKey?: string | null;
|
|
426
|
+
readonly readAt?: number | null;
|
|
427
|
+
readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
|
|
428
|
+
readonly wait?: CommitWait;
|
|
429
|
+
readonly timeout?: number;
|
|
430
|
+
}
|
|
431
|
+
export interface ResourceClient<T = Record<string, unknown>> {
|
|
432
|
+
retrieve(id: string, options?: ResourceReadOptions): Promise<ResourceRead<T>>;
|
|
433
|
+
create(data: Record<string, unknown>, options?: ResourceMutationOptions & {
|
|
434
|
+
readonly id?: string | null;
|
|
435
|
+
}): Promise<CommitReceipt>;
|
|
436
|
+
update(id: string, data: Record<string, unknown>, options?: ResourceMutationOptions): Promise<CommitReceipt>;
|
|
437
|
+
delete(id: string, options?: ResourceMutationOptions): Promise<CommitReceipt>;
|
|
438
|
+
}
|
|
439
|
+
/** The typed sync engine client — one property per model in the schema */
|
|
440
|
+
export type Ablo<S extends SchemaRecord> = {
|
|
441
|
+
readonly [K in keyof S & string]: ModelOperations<InferModel<Schema<S>, K>, InferCreate<Schema<S>, K>>;
|
|
442
|
+
} & {
|
|
443
|
+
/**
|
|
444
|
+
* Wait for the sync engine to finish its initial bootstrap.
|
|
445
|
+
* Resolves once entity data is loaded and the WebSocket is connected.
|
|
446
|
+
*
|
|
447
|
+
* ```ts
|
|
448
|
+
* const sync = Ablo({ schema, user });
|
|
449
|
+
* await sync.ready();
|
|
450
|
+
* const tasks = sync.tasks.findMany(); // data is available
|
|
451
|
+
* ```
|
|
452
|
+
*
|
|
453
|
+
* If bootstrap fails, this rejects with the underlying error (unreachable
|
|
454
|
+
* server, invalid API key, 500 from bootstrap endpoint, etc.).
|
|
455
|
+
*
|
|
456
|
+
* Idempotent — calling it multiple times returns the same promise.
|
|
457
|
+
*/
|
|
458
|
+
ready(): Promise<void>;
|
|
459
|
+
/**
|
|
460
|
+
* Wait for all pending mutations to be confirmed by the server.
|
|
461
|
+
*
|
|
462
|
+
* Sync engine mutations (`create`/`update`/`delete`) are optimistic and
|
|
463
|
+
* resolve immediately. Use this when you need to know the server has
|
|
464
|
+
* acknowledged everything before continuing — for example, before
|
|
465
|
+
* navigating away, before triggering a server-side workflow, or in tests.
|
|
466
|
+
*
|
|
467
|
+
* Resolves when `syncStatus.pendingChanges` reaches 0. If the engine is
|
|
468
|
+
* offline, this waits until reconnect + flush completes.
|
|
469
|
+
*
|
|
470
|
+
* ```ts
|
|
471
|
+
* await sync.tasks.create({ title: 'A' });
|
|
472
|
+
* await sync.tasks.create({ title: 'B' });
|
|
473
|
+
* await sync.waitForFlush(); // server has both tasks
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
476
|
+
* @param timeoutMs - Optional timeout. Default: no timeout (wait forever).
|
|
477
|
+
* Throws `Error('Flush timeout')` if reached with pending changes.
|
|
478
|
+
*/
|
|
479
|
+
waitForFlush(timeoutMs?: number): Promise<void>;
|
|
480
|
+
/** Disconnect and clean up */
|
|
481
|
+
dispose(): Promise<void>;
|
|
482
|
+
/**
|
|
483
|
+
* Destroy every IndexedDB database owned by this engine. Disconnects
|
|
484
|
+
* the WebSocket, releases timers, and deletes all `ablo_*` / `ablo-*`
|
|
485
|
+
* databases. Use on session expiry or explicit logout. Best-effort.
|
|
486
|
+
*/
|
|
487
|
+
purge(): Promise<void>;
|
|
488
|
+
/**
|
|
489
|
+
* Subscribe to session-error events (server rejected the session).
|
|
490
|
+
* Returns an unsubscribe function. Multiple subscribers supported.
|
|
491
|
+
* Typically called by `<AbloProvider>`, which calls `purge()` on fire
|
|
492
|
+
* and forwards to the consumer's `onSessionExpired` callback.
|
|
493
|
+
*/
|
|
494
|
+
onSessionError(listener: (error: Error) => void): () => void;
|
|
495
|
+
/**
|
|
496
|
+
* Subscribe to mutation failures with the full payload (transaction,
|
|
497
|
+
* error, permanent flag). Use this for user-visible failure surfaces —
|
|
498
|
+
* toasts keyed by `AbloError.type`, route-level "this entity reverted"
|
|
499
|
+
* boundaries, telemetry. Fires for both permanent rejections and
|
|
500
|
+
* `max_retries_exhausted` rollbacks.
|
|
501
|
+
*
|
|
502
|
+
* Distinct from `onSessionError` (server killed the session, requires
|
|
503
|
+
* re-auth) and from the `tx.isPersisted` per-call promise (call-site
|
|
504
|
+
* await, single transaction). This is the app-wide fan-in.
|
|
505
|
+
*/
|
|
506
|
+
onMutationFailure(listener: (payload: {
|
|
507
|
+
transaction: import('../transactions/TransactionQueue.js').Transaction;
|
|
508
|
+
error: Error;
|
|
509
|
+
permanent?: boolean;
|
|
510
|
+
}) => void): () => void;
|
|
511
|
+
/**
|
|
512
|
+
* Wait for the most-recent in-flight transaction for (modelName, modelId)
|
|
513
|
+
* to be confirmed by the server. Rejects with the same error that the
|
|
514
|
+
* queue's `transaction:failed` event would carry if the mutation is
|
|
515
|
+
* permanently rolled back. Resolves immediately when no transaction is
|
|
516
|
+
* in flight (already-confirmed or never-staged).
|
|
517
|
+
*
|
|
518
|
+
* Matches the queue's `'confirmed'` status vocabulary (see also
|
|
519
|
+
* `commits.create({wait:'confirmed'})`). Use this for the routing-
|
|
520
|
+
* grace-window pattern: stage a write, then
|
|
521
|
+
* `Promise.race([ablo.waitForConfirmation(...), gracePromise])` before
|
|
522
|
+
* navigating to a route whose URL depends on the optimistic id.
|
|
523
|
+
*/
|
|
524
|
+
waitForConfirmation(modelName: string, modelId: string): Promise<void>;
|
|
525
|
+
/**
|
|
526
|
+
* Reactive sync status — a MobX observable.
|
|
527
|
+
*
|
|
528
|
+
* Single source of truth for "what's the sync engine doing?" Contains:
|
|
529
|
+
* - `state`: `'idle' | 'syncing' | 'error' | 'offline' | 'reconnecting'`
|
|
530
|
+
* - `progress`: 0-100 for bootstrap progress
|
|
531
|
+
* - `error?`: Error object when `state === 'error'`
|
|
532
|
+
* - `pendingChanges`: Number of unconfirmed mutations in the queue
|
|
533
|
+
* - `lastSyncAt?`: Timestamp of the last successful delta processing
|
|
534
|
+
* - `offlineSince?`: When the connection dropped
|
|
535
|
+
* - `isSessionError`: True when the error requires re-authentication
|
|
536
|
+
*
|
|
537
|
+
* React components using `observer()` re-render automatically when
|
|
538
|
+
* any field changes — no manual subscription or polling needed.
|
|
539
|
+
*
|
|
540
|
+
* ```tsx
|
|
541
|
+
* import { observer } from 'mobx-react-lite';
|
|
542
|
+
*
|
|
543
|
+
* const SyncIndicator = observer(() => {
|
|
544
|
+
* if (sync.syncStatus.state === 'syncing') return <Spinner />;
|
|
545
|
+
* if (sync.syncStatus.state === 'error') return <Error msg={sync.syncStatus.error} />;
|
|
546
|
+
* if (sync.syncStatus.state === 'offline') return <OfflineBadge />;
|
|
547
|
+
* return null;
|
|
548
|
+
* });
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
readonly syncStatus: SyncStatus;
|
|
552
|
+
/** The underlying schema */
|
|
553
|
+
readonly schema: Schema<S>;
|
|
554
|
+
/**
|
|
555
|
+
* Real-time presence livestream — who else is connected on this
|
|
556
|
+
* engine's sync groups, what they're doing, and a write surface for
|
|
557
|
+
* announcing this user's own activity. Rides the engine's existing
|
|
558
|
+
* WebSocket; opening a participant for presence does NOT open a
|
|
559
|
+
* second socket. See `PresenceStream` in `types/streams.ts`.
|
|
560
|
+
*
|
|
561
|
+
* Stable reference for the engine's lifetime — the underlying
|
|
562
|
+
* connection is rotated on `dispose()` but this object is the same.
|
|
563
|
+
*/
|
|
564
|
+
readonly presence: PresenceStream;
|
|
565
|
+
/**
|
|
566
|
+
* Cooperative-mutex layer over presence — announce "I'm about to do
|
|
567
|
+
* X on Y" so peers can yield before colliding. Server enforces the
|
|
568
|
+
* mutex; rejected announcements surface via `intents.onRejected(...)`.
|
|
569
|
+
* Same socket as entity sync, no second connection.
|
|
570
|
+
*/
|
|
571
|
+
readonly intents: IntentResource;
|
|
572
|
+
/**
|
|
573
|
+
* Canonical low-level mutation API. Every resource convenience write
|
|
574
|
+
* compiles down to `commits.create(...)`.
|
|
575
|
+
*/
|
|
576
|
+
readonly commits: CommitResource;
|
|
577
|
+
/**
|
|
578
|
+
* Canonical untyped resource API. This is the portable API shape that maps
|
|
579
|
+
* cleanly to HTTP/Python/Ruby/Go clients. Typed `ablo.<model>` properties
|
|
580
|
+
* are schema-powered sugar over the same resource model.
|
|
581
|
+
*/
|
|
582
|
+
resource<T = Record<string, unknown>>(name: string): ResourceClient<T>;
|
|
583
|
+
/**
|
|
584
|
+
* Canonical multiplayer participant surface. Joins a structured app
|
|
585
|
+
* target, derives the transport scope internally, opens a scoped
|
|
586
|
+
* claim on the existing WebSocket, and returns target-bound presence
|
|
587
|
+
* + intent helpers.
|
|
588
|
+
*
|
|
589
|
+
* ```ts
|
|
590
|
+
* const participant = await ablo.participants.join({
|
|
591
|
+
* type: 'File',
|
|
592
|
+
* id: 'src/foo.ts',
|
|
593
|
+
* path: 'src/foo.ts',
|
|
594
|
+
* range: { startLine: 10, endLine: 40 },
|
|
595
|
+
* });
|
|
596
|
+
* participant.presence.editing();
|
|
597
|
+
* const claim = participant.intents.claim('rewrite imports');
|
|
598
|
+
* ```
|
|
599
|
+
*/
|
|
600
|
+
readonly participants: ParticipantManager;
|
|
601
|
+
/**
|
|
602
|
+
* Capture a context-staleness watermark over a set of entities.
|
|
603
|
+
* Returns a flat snapshot with `stamp` (thread into writes as
|
|
604
|
+
* `readAt`), `signal` (aborts on any captured-entity delta), and
|
|
605
|
+
* `onChange` (callback form). Reads from the engine's ObjectPool;
|
|
606
|
+
* subscription is on the engine's existing transport.
|
|
607
|
+
*
|
|
608
|
+
* Use before an LLM call to prevent the model from completing
|
|
609
|
+
* against now-stale data:
|
|
610
|
+
* ```ts
|
|
611
|
+
* const snap = engine.snapshot({ slides: deck.slideIds });
|
|
612
|
+
* await streamText({ messages, signal: snap.signal });
|
|
613
|
+
* ```
|
|
614
|
+
*/
|
|
615
|
+
snapshot<ModelName extends keyof S & string>(entities: {
|
|
616
|
+
readonly [M in ModelName]: string | readonly string[];
|
|
617
|
+
}): Snapshot<Schema<S>, ModelName>;
|
|
618
|
+
/**
|
|
619
|
+
* Open a turn — every commit issued while the returned handle is
|
|
620
|
+
* alive carries `caused_by_task_id` on the wire so the server
|
|
621
|
+
* stamps it onto each delta. Powers `agent_tasks` audit trails.
|
|
622
|
+
* Server: `POST /api/agent/turn` with the capability bearer.
|
|
623
|
+
*/
|
|
624
|
+
beginTurn(options: {
|
|
625
|
+
readonly prompt: string;
|
|
626
|
+
readonly parentTaskId?: string;
|
|
627
|
+
readonly surface?: string;
|
|
628
|
+
readonly metadata?: Record<string, unknown>;
|
|
629
|
+
}): Promise<Turn>;
|
|
630
|
+
/**
|
|
631
|
+
* The internal BaseSyncedStore. Implements SyncStoreContract — pass to
|
|
632
|
+
* SyncContext.Provider so the SDK's useModel/useModels/useMutations hooks
|
|
633
|
+
* can access it. Also satisfies useSyncStore() consumers during migration.
|
|
634
|
+
*/
|
|
635
|
+
readonly _store: SyncStoreContract;
|
|
636
|
+
/** The ObjectPool — for demand loaders and direct pool operations. */
|
|
637
|
+
readonly _pool: ObjectPool;
|
|
638
|
+
/**
|
|
639
|
+
* The SyncWebSocket handle — for collaboration events (slide selection,
|
|
640
|
+
* cursor broadcast). Null until the engine connects.
|
|
641
|
+
*/
|
|
642
|
+
readonly _ws: SyncWebSocket | null;
|
|
643
|
+
};
|
|
644
|
+
/**
|
|
645
|
+
* Compute a create-priority map from schema `belongsTo` relations using
|
|
646
|
+
* Tarjan's strongly-connected-components algorithm.
|
|
647
|
+
*
|
|
648
|
+
* The FK graph has an edge `child → parent` for every `belongsTo`. Tarjan
|
|
649
|
+
* runs a single linear DFS that simultaneously (a) detects cycles by
|
|
650
|
+
* grouping mutually-reachable nodes into SCCs and (b) emits those SCCs
|
|
651
|
+
* in reverse topological order of the condensation graph. In this edge
|
|
652
|
+
* convention a "sink" SCC has no outgoing edges — i.e. no parents — so
|
|
653
|
+
* it is an *FK root* (`organizations`, `themes`, etc.). Tarjan emits
|
|
654
|
+
* roots first and leaves last, exactly the order in which rows must be
|
|
655
|
+
* inserted to satisfy FK constraints.
|
|
656
|
+
*
|
|
657
|
+
* Priorities are assigned by emit order: SCC #0 → 10, SCC #1 → 20, …
|
|
658
|
+
* Members of the same SCC share a priority, so insertion order wins the
|
|
659
|
+
* tiebreak inside a cycle (this matters for cyclic schemas like
|
|
660
|
+
* `slideDecks ↔ layouts`, where one direction is the user's chosen
|
|
661
|
+
* "soft" edge — only the consumer's mutator sequence knows which one).
|
|
662
|
+
*
|
|
663
|
+
* This algorithm is iteration-order-independent: starting the DFS from
|
|
664
|
+
* any node yields the same SCC partitioning, and SCCs always come out
|
|
665
|
+
* in valid topological order. The previous DFS-with-memoization
|
|
666
|
+
* heuristic broke under cycles by treating the back-edge as depth 0,
|
|
667
|
+
* which made priorities depend on which node the walk happened to
|
|
668
|
+
* enter the cycle at.
|
|
669
|
+
*
|
|
670
|
+
* Schema authors can mark one side of a cycle with
|
|
671
|
+
* `belongsTo(target, fk, { defer: true })`. Those edges are excluded
|
|
672
|
+
* from the dependency graph entirely, which deterministically breaks
|
|
673
|
+
* the cycle and turns the SCC into a chain — the marked child gets a
|
|
674
|
+
* strictly higher priority than its parent instead of being tied with
|
|
675
|
+
* it. Pair with a Postgres `DEFERRABLE INITIALLY DEFERRED` constraint
|
|
676
|
+
* if you want the database side of the cycle to also relax. See
|
|
677
|
+
* {@link BelongsToOptions.defer}.
|
|
678
|
+
*
|
|
679
|
+
* The returned map is keyed by {@link ModelDef.typename} (falling back
|
|
680
|
+
* to the schema key), because that is what `Model.getModelName()`
|
|
681
|
+
* returns at transaction time — keying by schema key would silently
|
|
682
|
+
* miss the lookup and every model would fall through to
|
|
683
|
+
* `defaultCreatePriority`.
|
|
684
|
+
*
|
|
685
|
+
* Reference: Tarjan, R. (1972), "Depth-first search and linear graph
|
|
686
|
+
* algorithms." Linear in V + E.
|
|
687
|
+
*/
|
|
688
|
+
export declare function computeFKDepthPriority(schema: Schema): ReadonlyMap<string, number>;
|
|
689
|
+
/**
|
|
690
|
+
* Create a sync engine client in one call.
|
|
691
|
+
*
|
|
692
|
+
* ```ts
|
|
693
|
+
* const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
694
|
+
*
|
|
695
|
+
* const tasks = sync.tasks.list({ where: { status: 'todo' } });
|
|
696
|
+
* await sync.tasks.create({ title: 'New task' });
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
export declare function Ablo<const S extends SchemaRecord>(options: AbloOptions<S>): Ablo<S>;
|
|
700
|
+
export declare function Ablo(options: AbloApiClientOptions): AbloApi;
|
|
701
|
+
import type * as _Streams from '../types/streams.js';
|
|
702
|
+
import type * as _Participants from '../sync/participants.js';
|
|
703
|
+
import type * as _Policy from '../policy/types.js';
|
|
704
|
+
import type * as _Mutators from '../mutators/defineMutators.js';
|
|
705
|
+
import type * as _Tx from '../mutators/Transaction.js';
|
|
706
|
+
import type * as _Undo from '../mutators/UndoManager.js';
|
|
707
|
+
import type * as _SchemaTypes from '../schema/schema.js';
|
|
708
|
+
/**
|
|
709
|
+
* Canonical type namespace.
|
|
710
|
+
*
|
|
711
|
+
* Locked rules — apply uniformly to every future addition:
|
|
712
|
+
*
|
|
713
|
+
* 1. Flat by default. `Ablo.X`. Fewest dots wins.
|
|
714
|
+
* 2. Sub-namespace ONLY when (a) 4+ types share a single conceptual
|
|
715
|
+
* prefix, AND (b) names read better with the prefix (`Conflict.Kind`
|
|
716
|
+
* over `ConflictKind`). If the cluster is heterogeneous (streams +
|
|
717
|
+
* data + handles), keep flat.
|
|
718
|
+
* 3. Only types a consumer would write `: Ablo.X` for. Inferred-only
|
|
719
|
+
* types stay un-exported.
|
|
720
|
+
* 4. Wire shapes never on `Ablo.*`. Engine vocabulary only.
|
|
721
|
+
* 5. Advanced / framework-integration types stay internal unless they
|
|
722
|
+
* graduate into one of the public subpaths.
|
|
723
|
+
*
|
|
724
|
+
* Anything not on this list stays internal.
|
|
725
|
+
*/
|
|
726
|
+
export declare namespace Ablo {
|
|
727
|
+
type Options<S extends SchemaRecord = SchemaRecord> = AbloOptions<S>;
|
|
728
|
+
type Api = AbloApi;
|
|
729
|
+
type ApiIntents = AbloApiIntents;
|
|
730
|
+
type Agent = import('./ApiClient.js').Agent;
|
|
731
|
+
type AgentOptions = import('./ApiClient.js').AgentOptions;
|
|
732
|
+
type AgentRunOptions = import('./ApiClient.js').AgentRunOptions;
|
|
733
|
+
type AgentRunStatus = import('./ApiClient.js').AgentRunStatus;
|
|
734
|
+
type AgentRunResult<T> = import('./ApiClient.js').AgentRunResult<T>;
|
|
735
|
+
type AgentRunContext = import('./ApiClient.js').AgentRunContext;
|
|
736
|
+
type AgentResourceClient<T = Record<string, unknown>> = import('./ApiClient.js').AgentResourceClient<T>;
|
|
737
|
+
type AgentResourceReadOptions = import('./ApiClient.js').AgentResourceReadOptions;
|
|
738
|
+
type AgentResourceMutationOptions = import('./ApiClient.js').AgentResourceMutationOptions;
|
|
739
|
+
type AgentIntentOptions = import('./ApiClient.js').AgentIntentOptions;
|
|
740
|
+
type AgentIntentInput = import('./ApiClient.js').AgentIntentInput;
|
|
741
|
+
type Capability = import('./ApiClient.js').Capability;
|
|
742
|
+
type CapabilityCreateOptions = import('./ApiClient.js').CapabilityCreateOptions;
|
|
743
|
+
type CapabilityRecord = import('./ApiClient.js').CapabilityRecord;
|
|
744
|
+
type CapabilityResource = import('./ApiClient.js').CapabilityResource;
|
|
745
|
+
type CapabilityRevocation = import('./ApiClient.js').CapabilityRevocation;
|
|
746
|
+
type Task = import('./ApiClient.js').Task;
|
|
747
|
+
type TaskCreateOptions = import('./ApiClient.js').TaskCreateOptions;
|
|
748
|
+
type TaskCloseOptions = import('./ApiClient.js').TaskCloseOptions;
|
|
749
|
+
type TaskCloseResult = import('./ApiClient.js').TaskCloseResult;
|
|
750
|
+
type TaskResource = import('./ApiClient.js').TaskResource;
|
|
751
|
+
type BusyPolicy = import('./Ablo.js').BusyPolicy;
|
|
752
|
+
type BusyOptions = import('./Ablo.js').BusyOptions;
|
|
753
|
+
type EntityRef = _Streams.EntityRef;
|
|
754
|
+
type PresenceTarget = _Streams.PresenceTarget;
|
|
755
|
+
type TargetRange = _Streams.TargetRange;
|
|
756
|
+
type Duration = _Streams.Duration;
|
|
757
|
+
type PresenceStream = _Streams.PresenceStream;
|
|
758
|
+
type IntentStream = _Streams.IntentStream;
|
|
759
|
+
type Peer = _Streams.Peer;
|
|
760
|
+
type Activity = _Streams.Activity;
|
|
761
|
+
type ActiveIntent = _Streams.ActiveIntent;
|
|
762
|
+
type Claim = _Streams.Claim;
|
|
763
|
+
type IntentRejection = _Streams.IntentRejection;
|
|
764
|
+
type Snapshot<TSchema extends _SchemaTypes.Schema = _SchemaTypes.Schema, K extends keyof TSchema['models'] = keyof TSchema['models']> = _Streams.Snapshot<TSchema, K>;
|
|
765
|
+
type Turn = import('./Ablo.js').Turn;
|
|
766
|
+
namespace Auth {
|
|
767
|
+
type Principal = _Streams.Principal;
|
|
768
|
+
type Session = _Streams.SessionRef;
|
|
769
|
+
type Agent = _Streams.AgentRef;
|
|
770
|
+
type Actor = _Streams.ParticipantRef;
|
|
771
|
+
}
|
|
772
|
+
namespace Participant {
|
|
773
|
+
type Manager = _Participants.ParticipantManager;
|
|
774
|
+
type Joined = _Participants.JoinedParticipant;
|
|
775
|
+
type Scope = _Participants.ParticipantScope;
|
|
776
|
+
type Status = _Participants.ParticipantStatus;
|
|
777
|
+
type JoinOptions = _Participants.ParticipantJoinOptions;
|
|
778
|
+
}
|
|
779
|
+
type Schema<S extends _SchemaTypes.SchemaRecord = _SchemaTypes.SchemaRecord> = _SchemaTypes.Schema<S>;
|
|
780
|
+
namespace Schema {
|
|
781
|
+
type InferModel<S extends _SchemaTypes.Schema, K extends keyof S['models']> = _SchemaTypes.InferModel<S, K>;
|
|
782
|
+
type InferCreate<S extends _SchemaTypes.Schema, K extends keyof S['models']> = _SchemaTypes.InferCreate<S, K>;
|
|
783
|
+
type InferModelNames<S extends _SchemaTypes.Schema> = _SchemaTypes.InferModelNames<S>;
|
|
784
|
+
}
|
|
785
|
+
type Conflict = _Policy.Conflict;
|
|
786
|
+
namespace Conflict {
|
|
787
|
+
type Kind = _Policy.ConflictKind;
|
|
788
|
+
type Operation = _Policy.ConflictOperation;
|
|
789
|
+
type Decision = _Policy.ConflictDecision;
|
|
790
|
+
type Policy = _Policy.ConflictPolicy;
|
|
791
|
+
}
|
|
792
|
+
namespace Commit {
|
|
793
|
+
type Wait = import('./Ablo.js').CommitWait;
|
|
794
|
+
type OperationAction = import('./Ablo.js').ResourceOperationAction;
|
|
795
|
+
type OperationInput = import('./Ablo.js').CommitOperationInput;
|
|
796
|
+
type CreateOptions = import('./Ablo.js').CommitCreateOptions;
|
|
797
|
+
type Receipt = import('./Ablo.js').CommitReceipt;
|
|
798
|
+
type Resource = import('./Ablo.js').CommitResource;
|
|
799
|
+
}
|
|
800
|
+
namespace Intent {
|
|
801
|
+
type Handle = import('./Ablo.js').IntentHandle;
|
|
802
|
+
type CreateOptions = import('./Ablo.js').IntentCreateOptions;
|
|
803
|
+
type WaitOptions = import('./Ablo.js').IntentWaitOptions;
|
|
804
|
+
type Resource = import('./Ablo.js').IntentResource;
|
|
805
|
+
}
|
|
806
|
+
namespace Resource {
|
|
807
|
+
type Target = import('./Ablo.js').ResourceTarget;
|
|
808
|
+
type Intent = import('./Ablo.js').ResourceIntent;
|
|
809
|
+
type Read<T = Record<string, unknown>> = import('./Ablo.js').ResourceRead<T>;
|
|
810
|
+
type Client<T = Record<string, unknown>> = import('./Ablo.js').ResourceClient<T>;
|
|
811
|
+
type ReadOptions = import('./Ablo.js').ResourceReadOptions;
|
|
812
|
+
type MutationOptions = import('./Ablo.js').ResourceMutationOptions;
|
|
813
|
+
}
|
|
814
|
+
namespace Source {
|
|
815
|
+
type Operation = import('../source/index.js').SourceOperation;
|
|
816
|
+
type Event = import('../source/index.js').SourceEvent;
|
|
817
|
+
type EventsResult = import('../source/index.js').SourceEventsResult;
|
|
818
|
+
type Scope = import('../source/index.js').SourceScope;
|
|
819
|
+
type Secret = import('../source/index.js').SourceSecret;
|
|
820
|
+
type Options<S extends _SchemaTypes.SchemaRecord = _SchemaTypes.SchemaRecord, TAuth = unknown> = import('../source/index.js').AbloSourceOptions<S, TAuth>;
|
|
821
|
+
type ModelHandlers<Row, CreateInput, TAuth = unknown> = import('../source/index.js').SourceModelHandlers<Row, CreateInput, TAuth>;
|
|
822
|
+
type SignatureVerificationResult = import('../source/index.js').SourceSignatureVerificationResult;
|
|
823
|
+
namespace Commit {
|
|
824
|
+
type Params<TAuth = unknown> = import('../source/index.js').SourceCommitParams<TAuth>;
|
|
825
|
+
type Result<Row = Record<string, unknown>> = import('../source/index.js').SourceCommitResult<Row>;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
namespace Mutator {
|
|
829
|
+
type Fn<S extends _SchemaTypes.Schema, TArgs, TResult = void> = _Mutators.MutatorFn<S, TArgs, TResult>;
|
|
830
|
+
type Transaction<S extends _SchemaTypes.Schema> = _Tx.Transaction<S>;
|
|
831
|
+
type UndoEntry = _Undo.UndoEntry;
|
|
832
|
+
type UndoScope<S extends _SchemaTypes.Schema = _SchemaTypes.Schema> = _Undo.UndoScope<S>;
|
|
833
|
+
type InverseOp = _Undo.InverseOp;
|
|
834
|
+
}
|
|
835
|
+
}
|