@abloatai/ablo 0.25.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.
- package/AGENTS.md +5 -3
- package/CHANGELOG.md +20 -0
- package/README.md +3 -3
- package/dist/BaseSyncedStore.d.ts +72 -195
- package/dist/BaseSyncedStore.js +240 -625
- package/dist/Database.d.ts +8 -9
- package/dist/Database.js +9 -14
- package/dist/LazyReferenceCollection.js +5 -0
- package/dist/Model.js +6 -8
- package/dist/ModelRegistry.d.ts +25 -6
- package/dist/ModelRegistry.js +12 -6
- package/dist/NetworkMonitor.js +1 -1
- package/dist/ObjectPool.js +9 -3
- package/dist/SyncClient.d.ts +15 -5
- package/dist/SyncClient.js +152 -66
- package/dist/SyncEngineContext.d.ts +1 -3
- package/dist/SyncEngineContext.js +1 -2
- package/dist/agent/Agent.d.ts +21 -8
- package/dist/agent/Agent.js +26 -13
- package/dist/ai-sdk/coordination-context.js +9 -3
- package/dist/ai-sdk/wrap.d.ts +1 -1
- package/dist/ai-sdk/wrap.js +1 -1
- package/dist/auth/credentialPolicy.d.ts +10 -2
- package/dist/auth/credentialSource.d.ts +1 -13
- package/dist/auth/credentialSource.js +6 -13
- package/dist/auth/index.js +3 -3
- package/dist/cli.cjs +328 -262
- package/dist/client/Ablo.d.ts +35 -764
- package/dist/client/Ablo.js +64 -688
- package/dist/client/ApiClient.d.ts +14 -1
- package/dist/client/ApiClient.js +63 -9
- package/dist/client/auth.d.ts +8 -17
- package/dist/client/auth.js +42 -6
- package/dist/client/consoleLogger.d.ts +36 -0
- package/dist/client/consoleLogger.js +45 -0
- package/dist/client/createInternalComponents.js +1 -1
- package/dist/client/createModelProxy.js +10 -6
- package/dist/client/credentialEndpoint.d.ts +63 -0
- package/dist/client/credentialEndpoint.js +87 -0
- package/dist/client/hostedEndpoints.d.ts +24 -0
- package/dist/client/hostedEndpoints.js +24 -0
- package/dist/client/httpClient.d.ts +1 -1
- package/dist/client/identity.js +2 -1
- package/dist/client/modelRegistration.d.ts +14 -0
- package/dist/client/modelRegistration.js +321 -0
- package/dist/client/options.d.ts +396 -0
- package/dist/client/options.js +10 -0
- package/dist/client/resourceTypes.d.ts +344 -0
- package/dist/client/resourceTypes.js +13 -0
- package/dist/client/schemaConfig.d.ts +56 -0
- package/dist/client/schemaConfig.js +188 -0
- package/dist/client/sessionMint.d.ts +1 -1
- package/dist/client/validateAbloOptions.js +1 -1
- package/dist/client/wsMutationExecutor.d.ts +33 -0
- package/dist/client/wsMutationExecutor.js +77 -0
- package/dist/context.js +0 -3
- package/dist/coordination/index.d.ts +8 -1
- package/dist/coordination/index.js +24 -1
- package/dist/coordination/trace.d.ts +0 -1
- package/dist/coordination/trace.js +0 -1
- package/dist/core/DatabaseManager.d.ts +0 -1
- package/dist/core/DatabaseManager.js +26 -24
- package/dist/core/QueryProcessor.js +7 -8
- package/dist/core/QueryView.d.ts +14 -2
- package/dist/core/QueryView.js +4 -5
- package/dist/core/StoreManager.d.ts +2 -2
- package/dist/core/StoreManager.js +5 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/openIDBWithTimeout.js +15 -11
- package/dist/core/query-utils.js +1 -1
- package/dist/core/storeContract.d.ts +143 -0
- package/dist/core/storeContract.js +16 -0
- package/dist/errorCodes.d.ts +11 -2
- package/dist/errorCodes.js +155 -137
- package/dist/errors.d.ts +18 -7
- package/dist/errors.js +14 -1
- package/dist/index.js +5 -0
- package/dist/interfaces/index.d.ts +6 -20
- package/dist/keys/index.js +18 -9
- package/dist/mutators/UndoManager.js +6 -1
- package/dist/mutators/defineMutators.d.ts +1 -3
- package/dist/policy/types.d.ts +1 -1
- package/dist/query/client.d.ts +11 -0
- package/dist/query/client.js +81 -52
- package/dist/react/AbloProvider.d.ts +6 -10
- package/dist/react/AbloProvider.js +22 -15
- package/dist/react/context.d.ts +2 -118
- package/dist/react/useAblo.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useMutators.js +7 -1
- package/dist/react/useReactive.js +0 -1
- package/dist/react/useUndoScope.js +2 -2
- package/dist/schema/ddlLock.d.ts +39 -0
- package/dist/schema/ddlLock.js +51 -0
- package/dist/schema/field.js +1 -1
- package/dist/schema/index.d.ts +4 -3
- package/dist/schema/index.js +15 -5
- package/dist/schema/model.d.ts +3 -3
- package/dist/schema/model.js +2 -2
- package/dist/schema/openapi.js +5 -1
- package/dist/schema/queries.d.ts +1 -1
- package/dist/schema/queries.js +1 -0
- package/dist/schema/residency.d.ts +35 -0
- package/dist/schema/residency.js +27 -0
- package/dist/schema/roles.d.ts +25 -0
- package/dist/schema/roles.js +37 -0
- package/dist/schema/schema.d.ts +3 -3
- package/dist/schema/schema.js +11 -7
- package/dist/schema/serialize.d.ts +8 -8
- package/dist/schema/sync-delta-row.d.ts +2 -2
- package/dist/schema/sync-delta-row.js +2 -2
- package/dist/server/storage-mode.d.ts +8 -1
- package/dist/server/storage-mode.js +14 -4
- package/dist/source/adapter.d.ts +1 -1
- package/dist/source/adapters/drizzle.js +4 -3
- package/dist/source/adapters/kysely.js +4 -3
- package/dist/source/adapters/memory.js +1 -1
- package/dist/source/adapters/prisma.js +5 -4
- package/dist/source/conformance.js +10 -8
- package/dist/source/connector.js +4 -2
- package/dist/source/factory.d.ts +106 -0
- package/dist/source/factory.js +273 -0
- package/dist/source/index.d.ts +11 -461
- package/dist/source/index.js +14 -416
- package/dist/source/next.d.ts +1 -1
- package/dist/source/next.js +1 -1
- package/dist/source/pushQueue.d.ts +1 -1
- package/dist/source/pushQueue.js +6 -3
- package/dist/source/signing.d.ts +59 -0
- package/dist/source/signing.js +142 -0
- package/dist/source/types.d.ts +334 -0
- package/dist/source/types.js +44 -0
- package/dist/stores/ObjectStore.d.ts +1 -1
- package/dist/stores/ObjectStore.js +23 -23
- package/dist/stores/SyncActionStore.d.ts +1 -1
- package/dist/stores/SyncActionStore.js +70 -35
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +1 -0
- package/dist/sync/AreaOfInterestManager.d.ts +2 -2
- package/dist/sync/AreaOfInterestManager.js +1 -1
- package/dist/sync/BootstrapHelper.d.ts +1 -3
- package/dist/sync/BootstrapHelper.js +29 -8
- package/dist/sync/ConnectionManager.d.ts +8 -0
- package/dist/sync/ConnectionManager.js +19 -4
- package/dist/sync/HydrationCoordinator.d.ts +12 -0
- package/dist/sync/HydrationCoordinator.js +25 -5
- package/dist/sync/NetworkProbe.d.ts +3 -2
- package/dist/sync/NetworkProbe.js +7 -6
- package/dist/sync/SyncWebSocket.d.ts +71 -120
- package/dist/sync/SyncWebSocket.js +324 -630
- package/dist/sync/awaitClaimGrant.js +27 -19
- package/dist/sync/bootstrapApply.d.ts +60 -0
- package/dist/sync/bootstrapApply.js +65 -0
- package/dist/sync/commitFrames.d.ts +43 -0
- package/dist/sync/commitFrames.js +94 -0
- package/dist/sync/createClaimStream.js +3 -2
- package/dist/sync/createSnapshot.d.ts +1 -3
- package/dist/sync/credentialLifecycle.d.ts +175 -0
- package/dist/sync/credentialLifecycle.js +329 -0
- package/dist/sync/deltaPipeline.d.ts +111 -0
- package/dist/sync/deltaPipeline.js +262 -0
- package/dist/sync/groupChange.d.ts +111 -0
- package/dist/sync/groupChange.js +246 -0
- package/dist/sync/heartbeat.d.ts +62 -0
- package/dist/sync/heartbeat.js +91 -0
- package/dist/sync/participants.d.ts +8 -8
- package/dist/sync/syncCursor.d.ts +44 -0
- package/dist/sync/syncCursor.js +59 -0
- package/dist/sync/syncPlan.d.ts +62 -0
- package/dist/sync/syncPlan.js +51 -0
- package/dist/sync/wsFrameHandlers.d.ts +117 -0
- package/dist/sync/wsFrameHandlers.js +378 -0
- package/dist/testing/fixtures/bootstrap.d.ts +9 -11
- package/dist/testing/fixtures/deltas.d.ts +1 -1
- package/dist/testing/fixtures/models.js +1 -0
- package/dist/testing/helpers/sync-engine-harness.d.ts +0 -3
- package/dist/testing/helpers/sync-engine-harness.js +2 -1
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/testing/mocks/MockSyncContext.d.ts +1 -17
- package/dist/testing/mocks/MockSyncContext.js +1 -32
- package/dist/testing/mocks/MockSyncStore.d.ts +4 -4
- package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
- package/dist/transactions/TransactionQueue.d.ts +20 -56
- package/dist/transactions/TransactionQueue.js +101 -487
- package/dist/transactions/TransactionStore.d.ts +18 -0
- package/dist/transactions/TransactionStore.js +51 -0
- package/dist/transactions/coalesceRules.d.ts +34 -0
- package/dist/transactions/coalesceRules.js +117 -0
- package/dist/transactions/commitPayload.d.ts +134 -0
- package/dist/transactions/commitPayload.js +152 -0
- package/dist/transactions/deltaConfirmation.d.ts +60 -0
- package/dist/transactions/deltaConfirmation.js +223 -0
- package/dist/transactions/optimistic.d.ts +24 -0
- package/dist/transactions/optimistic.js +45 -0
- package/dist/transactions/persistedReplay.d.ts +93 -0
- package/dist/transactions/persistedReplay.js +105 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +0 -5
- package/dist/types/modelData.d.ts +12 -0
- package/dist/types/modelData.js +11 -0
- package/dist/types/participant.d.ts +21 -0
- package/dist/types/participant.js +12 -0
- package/dist/types/streams.d.ts +8 -14
- package/dist/utils/mobx-setup.d.ts +1 -1
- package/dist/utils/mobx-setup.js +2 -2
- package/dist/wire/errorEnvelope.d.ts +11 -2
- package/dist/wire/errorEnvelope.js +14 -2
- package/dist/wire/frames.d.ts +90 -0
- package/dist/wire/frames.js +55 -1
- package/dist/wire/index.d.ts +4 -1
- package/dist/wire/index.js +20 -1
- package/dist/wire/protocol.d.ts +45 -0
- package/dist/wire/protocol.js +45 -0
- package/dist/wire/protocolVersion.d.ts +56 -0
- package/dist/wire/protocolVersion.js +63 -0
- package/docs/api-keys.md +4 -3
- package/docs/examples/existing-python-backend.md +3 -3
- package/docs/identity.md +4 -4
- package/docs/integration-guide.md +1 -1
- package/docs/react.md +1 -1
- package/docs/sessions.md +5 -7
- package/package.json +15 -13
- package/dist/client/index.d.ts +0 -36
- package/dist/client/index.js +0 -33
- package/dist/config/index.d.ts +0 -10
- package/dist/config/index.js +0 -12
- package/dist/interfaces/headless.d.ts +0 -95
- package/dist/interfaces/headless.js +0 -41
- package/dist/query/index.d.ts +0 -6
- package/dist/query/index.js +0 -5
- package/dist/realtime/index.d.ts +0 -10
- package/dist/realtime/index.js +0 -9
- package/dist/schema/plane.d.ts +0 -23
- package/dist/schema/plane.js +0 -19
- package/dist/server/next.d.ts +0 -51
- package/dist/server/next.js +0 -47
- package/dist/sync/OfflineFlush.d.ts +0 -9
- package/dist/sync/OfflineFlush.js +0 -22
- package/dist/sync/OfflineTransactionStore.d.ts +0 -37
- package/dist/sync/OfflineTransactionStore.js +0 -263
- package/dist/transactions/index.d.ts +0 -16
- package/dist/transactions/index.js +0 -7
package/dist/client/Ablo.d.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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('./
|
|
1095
|
-
type ClaimedOptions = import('./
|
|
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('./
|
|
1134
|
-
type OperationAction = import('./
|
|
1135
|
-
type OperationInput = import('./
|
|
1136
|
-
type CreateOptions = import('./
|
|
1137
|
-
type Receipt = import('./
|
|
1138
|
-
type Client = import('./
|
|
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('./
|
|
1142
|
-
type CreateOptions = import('./
|
|
1143
|
-
type WaitOptions = import('./
|
|
1144
|
-
type Client = import('./
|
|
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('./
|
|
1148
|
-
type Claim = import('./
|
|
1149
|
-
type Read<T = Record<string, unknown>> = import('./
|
|
1150
|
-
type Client<T = Record<string, unknown>> = import('./
|
|
1151
|
-
type ReadOptions = import('./
|
|
1152
|
-
type MutationOptions = import('./
|
|
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;
|