@abloatai/ablo 0.7.0 → 0.9.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 +72 -1
- package/README.md +80 -66
- package/dist/BaseSyncedStore.d.ts +73 -0
- package/dist/BaseSyncedStore.js +179 -5
- package/dist/Model.d.ts +42 -0
- package/dist/Model.js +103 -44
- package/dist/SyncEngineContext.d.ts +2 -1
- package/dist/SyncEngineContext.js +5 -3
- package/dist/agent/session.js +6 -5
- package/dist/ai-sdk/coordination-context.js +4 -0
- package/dist/ai-sdk/index.d.ts +56 -47
- package/dist/ai-sdk/index.js +56 -47
- package/dist/ai-sdk/intent-broadcast.d.ts +5 -0
- package/dist/ai-sdk/intent-broadcast.js +11 -4
- package/dist/ai-sdk/wrap.d.ts +14 -11
- package/dist/ai-sdk/wrap.js +11 -13
- package/dist/auth/credentialSource.d.ts +34 -0
- package/dist/auth/credentialSource.js +63 -0
- package/dist/auth/index.d.ts +2 -22
- package/dist/auth/index.js +26 -36
- package/dist/auth/schemas.d.ts +35 -0
- package/dist/auth/schemas.js +53 -0
- package/dist/client/Ablo.d.ts +259 -33
- package/dist/client/Ablo.js +276 -73
- package/dist/client/ApiClient.d.ts +52 -4
- package/dist/client/ApiClient.js +236 -66
- package/dist/client/auth.d.ts +21 -2
- package/dist/client/auth.js +77 -5
- package/dist/client/createInternalComponents.d.ts +2 -0
- package/dist/client/createInternalComponents.js +8 -1
- package/dist/client/createModelProxy.d.ts +187 -79
- package/dist/client/createModelProxy.js +203 -68
- package/dist/client/httpClient.d.ts +71 -0
- package/dist/client/httpClient.js +69 -0
- package/dist/client/identity.d.ts +2 -6
- package/dist/client/identity.js +63 -11
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/registerDataSource.d.ts +19 -0
- package/dist/client/registerDataSource.js +59 -0
- package/dist/client/validateAbloOptions.d.ts +2 -1
- package/dist/client/validateAbloOptions.js +8 -7
- package/dist/core/DatabaseManager.js +30 -2
- package/dist/core/openIDBWithTimeout.d.ts +36 -0
- package/dist/core/openIDBWithTimeout.js +88 -1
- package/dist/errorCodes.d.ts +92 -1
- package/dist/errorCodes.js +139 -7
- package/dist/errors.d.ts +54 -3
- package/dist/errors.js +192 -44
- package/dist/index.d.ts +23 -10
- package/dist/index.js +21 -8
- package/dist/keys/index.d.ts +76 -0
- package/dist/keys/index.js +171 -0
- package/dist/mutators/UndoManager.d.ts +86 -50
- package/dist/mutators/UndoManager.js +129 -22
- package/dist/mutators/inverseOp.d.ts +129 -0
- package/dist/mutators/inverseOp.js +74 -0
- package/dist/mutators/readerActions.d.ts +1 -1
- package/dist/mutators/undoApply.d.ts +42 -0
- package/dist/mutators/undoApply.js +143 -0
- package/dist/query/client.d.ts +10 -9
- package/dist/query/client.js +22 -14
- package/dist/react/AbloProvider.d.ts +23 -101
- package/dist/react/AbloProvider.js +61 -103
- package/dist/react/ClientSideSuspense.d.ts +1 -1
- package/dist/react/DefaultFallback.d.ts +1 -1
- package/dist/react/SyncGroupProvider.d.ts +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +3 -2
- package/dist/react/useAblo.d.ts +4 -4
- package/dist/react/useAblo.js +10 -5
- package/dist/react/useCurrentUserId.d.ts +1 -1
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useMutators.js +19 -12
- package/dist/react/useReactive.js +16 -3
- package/dist/schema/ddl.d.ts +26 -3
- package/dist/schema/ddl.js +152 -4
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.js +12 -0
- package/dist/schema/model.d.ts +11 -0
- package/dist/schema/model.js +2 -0
- package/dist/schema/openapi.d.ts +28 -0
- package/dist/schema/openapi.js +118 -0
- package/dist/schema/plane.d.ts +23 -0
- package/dist/schema/plane.js +19 -0
- package/dist/schema/relation.d.ts +20 -0
- package/dist/schema/serialize.d.ts +7 -3
- package/dist/schema/serialize.js +6 -2
- package/dist/schema/sync-delta-row.d.ts +157 -0
- package/dist/schema/sync-delta-row.js +102 -0
- package/dist/schema/sync-delta-wire.d.ts +180 -0
- package/dist/schema/sync-delta-wire.js +102 -0
- package/dist/server/adapter.d.ts +156 -0
- package/dist/server/adapter.js +19 -0
- package/dist/server/commit.d.ts +82 -0
- package/dist/server/commit.js +1 -0
- package/dist/server/index.d.ts +14 -0
- package/dist/server/index.js +1 -0
- package/dist/server/next.d.ts +51 -0
- package/dist/server/next.js +47 -0
- package/dist/server/read-config.d.ts +60 -0
- package/dist/server/read-config.js +8 -0
- package/dist/server/storage-mode.d.ts +17 -0
- package/dist/server/storage-mode.js +12 -0
- package/dist/source/adapter.d.ts +59 -0
- package/dist/source/adapter.js +19 -0
- package/dist/source/adapters/drizzle.d.ts +34 -0
- package/dist/source/adapters/drizzle.js +147 -0
- package/dist/source/adapters/memory.d.ts +12 -0
- package/dist/source/adapters/memory.js +114 -0
- package/dist/source/adapters/prisma.d.ts +57 -0
- package/dist/source/adapters/prisma.js +199 -0
- package/dist/source/conformance.d.ts +32 -0
- package/dist/source/conformance.js +134 -0
- package/dist/source/contract.d.ts +143 -0
- package/dist/source/contract.js +98 -0
- package/dist/source/index.d.ts +61 -10
- package/dist/source/index.js +98 -0
- package/dist/source/next.d.ts +33 -0
- package/dist/source/next.js +26 -0
- package/dist/sync/BootstrapHelper.d.ts +10 -0
- package/dist/sync/BootstrapHelper.js +56 -42
- package/dist/sync/ConnectionManager.d.ts +57 -1
- package/dist/sync/ConnectionManager.js +186 -11
- package/dist/sync/HydrationCoordinator.d.ts +93 -17
- package/dist/sync/HydrationCoordinator.js +241 -41
- package/dist/sync/NetworkProbe.d.ts +60 -18
- package/dist/sync/NetworkProbe.js +121 -23
- package/dist/sync/SyncWebSocket.d.ts +45 -70
- package/dist/sync/SyncWebSocket.js +113 -89
- package/dist/sync/createIntentStream.js +10 -1
- package/dist/sync/participants.js +5 -2
- package/dist/transactions/TransactionQueue.js +13 -1
- package/dist/types/streams.d.ts +9 -0
- package/dist/utils/mobx-setup.js +1 -0
- package/dist/webhooks/events.d.ts +38 -0
- package/dist/webhooks/events.js +40 -0
- package/dist/webhooks/index.d.ts +10 -0
- package/dist/webhooks/index.js +10 -0
- package/dist/wire/errorEnvelope.d.ts +34 -0
- package/dist/wire/errorEnvelope.js +86 -0
- package/dist/wire/frames.d.ts +119 -0
- package/dist/wire/frames.js +1 -0
- package/dist/wire/index.d.ts +24 -0
- package/dist/wire/index.js +21 -0
- package/dist/wire/listEnvelope.d.ts +45 -0
- package/dist/wire/listEnvelope.js +17 -0
- package/docs/api-keys.md +5 -5
- package/docs/api.md +125 -65
- package/docs/audit.md +16 -9
- package/docs/cli.md +57 -47
- package/docs/client-behavior.md +54 -40
- package/docs/coordination.md +66 -80
- package/docs/data-sources.md +56 -34
- package/docs/examples/agent-human.md +74 -28
- package/docs/examples/ai-sdk-tool.md +29 -22
- package/docs/examples/existing-python-backend.md +41 -26
- package/docs/examples/nextjs.md +32 -17
- package/docs/examples/scoped-agent.md +43 -28
- package/docs/examples/server-agent.md +40 -15
- package/docs/guarantees.md +38 -27
- package/docs/identity.md +65 -59
- package/docs/index.md +30 -19
- package/docs/integration-guide.md +78 -78
- package/docs/interaction-model.md +43 -35
- package/docs/mcp/claude-code.md +11 -19
- package/docs/mcp/cursor.md +7 -25
- package/docs/mcp/windsurf.md +7 -20
- package/docs/mcp.md +103 -26
- package/docs/quickstart.md +63 -61
- package/docs/react.md +24 -16
- package/docs/roadmap.md +13 -13
- package/docs/schema-contract.md +111 -0
- package/docs/the-loop.md +21 -0
- package/examples/README.md +8 -4
- package/examples/data-source/README.md +10 -7
- package/examples/data-source/customer-server.ts +27 -25
- package/examples/data-source/run.ts +4 -3
- package/examples/quickstart.ts +1 -1
- package/llms.txt +55 -21
- package/package.json +48 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AbloAuthenticationError } from '../errors.js';
|
|
3
|
+
const AuthParticipantKindSchema = z.enum(['user', 'agent', 'system']);
|
|
4
|
+
export const AuthTokenSchema = z.string().trim().min(1);
|
|
5
|
+
export const CapabilityExchangeResponseSchema = z
|
|
6
|
+
.object({
|
|
7
|
+
capabilityId: z.string().min(1),
|
|
8
|
+
token: AuthTokenSchema,
|
|
9
|
+
expiresAt: z.string().min(1),
|
|
10
|
+
organizationId: z.string().min(1),
|
|
11
|
+
scope: z
|
|
12
|
+
.object({
|
|
13
|
+
organizationId: z.string().min(1),
|
|
14
|
+
syncGroups: z.array(z.string()),
|
|
15
|
+
operations: z.array(z.string()),
|
|
16
|
+
participantKind: AuthParticipantKindSchema,
|
|
17
|
+
participantId: z.string().min(1),
|
|
18
|
+
})
|
|
19
|
+
.passthrough(),
|
|
20
|
+
userMeta: z.record(z.string(), z.unknown()),
|
|
21
|
+
})
|
|
22
|
+
.passthrough();
|
|
23
|
+
export const IdentityResolveResponseSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
participantKind: AuthParticipantKindSchema,
|
|
26
|
+
participantId: z.string().min(1),
|
|
27
|
+
accountScope: z.string().min(1),
|
|
28
|
+
syncGroups: z.array(z.string()),
|
|
29
|
+
userMeta: z.record(z.string(), z.unknown()),
|
|
30
|
+
})
|
|
31
|
+
.passthrough();
|
|
32
|
+
function formatIssues(error) {
|
|
33
|
+
return error.issues
|
|
34
|
+
.map((issue) => {
|
|
35
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : '<root>';
|
|
36
|
+
return `${path}: ${issue.message}`;
|
|
37
|
+
})
|
|
38
|
+
.join('; ');
|
|
39
|
+
}
|
|
40
|
+
export function parseCapabilityExchangeResponse(raw) {
|
|
41
|
+
const parsed = CapabilityExchangeResponseSchema.safeParse(raw);
|
|
42
|
+
if (!parsed.success) {
|
|
43
|
+
throw new AbloAuthenticationError(`apiKey exchange response was malformed: ${formatIssues(parsed.error)}`, { code: 'exchange_malformed_response', cause: parsed.error });
|
|
44
|
+
}
|
|
45
|
+
return parsed.data;
|
|
46
|
+
}
|
|
47
|
+
export function parseIdentityResolveResponse(raw) {
|
|
48
|
+
const parsed = IdentityResolveResponseSchema.safeParse(raw);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
throw new AbloAuthenticationError(`identity resolve response was malformed: ${formatIssues(parsed.error)}`, { code: 'identity_resolve_failed', cause: parsed.error });
|
|
51
|
+
}
|
|
52
|
+
return parsed.data;
|
|
53
|
+
}
|
package/dist/client/Ablo.d.ts
CHANGED
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
* const sync = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
12
12
|
*
|
|
13
13
|
* const reports = sync.reports.list({ where: { status: 'todo' } });
|
|
14
|
-
* await sync.reports.create({ title: 'Fix bug' });
|
|
15
|
-
* await sync.reports.update(
|
|
16
|
-
*
|
|
14
|
+
* await sync.reports.create({ data: { title: 'Fix bug' } });
|
|
15
|
+
* await sync.reports.update({
|
|
16
|
+
* id: reportId,
|
|
17
|
+
* data: { status: 'ready' },
|
|
18
|
+
* });
|
|
19
|
+
* await sync.reports.delete({ id: reportId });
|
|
17
20
|
*/
|
|
18
21
|
import type { Schema, SchemaRecord, InferModel, InferCreate } from '../schema/schema.js';
|
|
19
22
|
import type { SyncEngineConfig, SyncLogger, MutationExecutor, MutationDispatcher, SyncObservabilityProvider, SyncAnalytics, SessionErrorDetector, OnlineStatusProvider } from '../interfaces/index.js';
|
|
@@ -23,7 +26,7 @@ import type { SyncWebSocket } from '../sync/SyncWebSocket.js';
|
|
|
23
26
|
import { type SyncStatus } from '../BaseSyncedStore.js';
|
|
24
27
|
import type { IntentStream, IntentWaitOptions, PresenceStream, Snapshot } from '../types/streams.js';
|
|
25
28
|
import type { ParticipantManager } from '../sync/participants.js';
|
|
26
|
-
import type { ActiveIntent, Duration, TargetRange } from '../types/streams.js';
|
|
29
|
+
import type { ActiveIntent, Duration, Intent, TargetRange } from '../types/streams.js';
|
|
27
30
|
import { type AbloApi, type AbloApiClientOptions, type AbloApiIntents } from './ApiClient.js';
|
|
28
31
|
/**
|
|
29
32
|
* Handle returned by `engine.beginTurn()`. While alive, every commit
|
|
@@ -71,7 +74,7 @@ import { type AbloPersistence } from './persistence.js';
|
|
|
71
74
|
* the way you'd reach for the equivalent option on the Stripe / OpenAI
|
|
72
75
|
* / Anthropic clients: rarely, and deliberately.
|
|
73
76
|
*
|
|
74
|
-
* @see https://docs.
|
|
77
|
+
* @see https://docs.abloatai.com — full option reference
|
|
75
78
|
*/
|
|
76
79
|
export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
77
80
|
/**
|
|
@@ -81,13 +84,55 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
81
84
|
*/
|
|
82
85
|
schema: Schema<S>;
|
|
83
86
|
/**
|
|
84
|
-
* API key
|
|
87
|
+
* API key — **the one auth field most apps set.** Server-side this is your
|
|
88
|
+
* secret `sk_` (and it defaults to `process.env['ABLO_API_KEY']`, so you
|
|
89
|
+
* usually pass nothing). A long-lived key needs no refresh; the client uses
|
|
90
|
+
* it as-is.
|
|
85
91
|
*
|
|
86
|
-
* Accepts a static string
|
|
87
|
-
*
|
|
88
|
-
*
|
|
92
|
+
* Accepts a static string or an async `() => Promise<string>` resolver if you
|
|
93
|
+
* rotate keys out-of-band (resolved at bootstrap).
|
|
94
|
+
*
|
|
95
|
+
* Browser apps that mint a SHORT-LIVED per-user key (`ek_`) from a login can't
|
|
96
|
+
* ship a secret — those use {@link getToken} (or {@link authEndpoint}) instead,
|
|
97
|
+
* which the client refreshes for you. That's the only case that isn't "just
|
|
98
|
+
* `apiKey`".
|
|
89
99
|
*/
|
|
90
100
|
apiKey?: string | ApiKeySetter | null | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Opt-in for the SHORT-LIVED per-user browser case: an async resolver for a
|
|
103
|
+
* fresh bearer (`ek_`/`rk_`) your backend minted for the signed-in user. The
|
|
104
|
+
* client calls it once before connect and then keeps the key fresh for you —
|
|
105
|
+
* a refresh timer ahead of expiry plus re-mint on OS-wake / network-online /
|
|
106
|
+
* tab-focus, and a reactive re-mint when a probe finds the key stale. You
|
|
107
|
+
* never call a refresh method (Supabase `autoRefreshToken` model).
|
|
108
|
+
*
|
|
109
|
+
* Contract: resolve a token, resolve `null` when the login itself is gone
|
|
110
|
+
* (terminal → sign out), or THROW on a transient failure (→ back off, never
|
|
111
|
+
* sign out). Leave unset for the static-`apiKey` path.
|
|
112
|
+
*/
|
|
113
|
+
getToken?: (() => Promise<string | null>) | undefined;
|
|
114
|
+
/**
|
|
115
|
+
* Convenience over {@link getToken}: a URL on YOUR backend that returns
|
|
116
|
+
* `{ token }`. The client POSTs to it (with cookies, so it's authed by the
|
|
117
|
+
* user's session) to mint + refresh the bearer. Ignored when `getToken` is
|
|
118
|
+
* set. Pure sugar — `getToken: () => fetch(url).then(r => r.json()).then(b => b.token)`.
|
|
119
|
+
*/
|
|
120
|
+
authEndpoint?: string | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* Direct-URL convenience connector: a connection string to your own Postgres
|
|
123
|
+
* that Ablo can register for a dedicated tenant.
|
|
124
|
+
*
|
|
125
|
+
* This is NOT the default Data Source path. For the Zero-shaped default, keep
|
|
126
|
+
* `DATABASE_URL` in your app, expose `dataSource(...)`, and let your server
|
|
127
|
+
* write the database while Ablo coordinates the sync stream.
|
|
128
|
+
*
|
|
129
|
+
* SERVER-ONLY: this carries credentials, so it is never sent from the browser
|
|
130
|
+
* — constructing a client with `databaseUrl` and `dangerouslyAllowBrowser`
|
|
131
|
+
* throws. If you opt into this connector, provide a NON-superuser,
|
|
132
|
+
* non-`BYPASSRLS` role; the direct connector rejects privileged roles that
|
|
133
|
+
* cannot enforce RLS.
|
|
134
|
+
*/
|
|
135
|
+
databaseUrl?: string | null | undefined;
|
|
91
136
|
/**
|
|
92
137
|
* Local persistence mode. Pass `indexeddb` only when you want offline
|
|
93
138
|
* queueing and a reload-surviving browser cache.
|
|
@@ -112,8 +157,8 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
112
157
|
defaultQuery?: Record<string, string | undefined> | undefined;
|
|
113
158
|
/**
|
|
114
159
|
* Client-side use is disabled by default because private API keys should
|
|
115
|
-
* not ship to browsers. Set this only when
|
|
116
|
-
*
|
|
160
|
+
* not ship to browsers. Set this only when the browser holds a minted
|
|
161
|
+
* session token (`ek_`/`rk_`) or you route through a controlled server proxy.
|
|
117
162
|
*/
|
|
118
163
|
dangerouslyAllowBrowser?: boolean | undefined;
|
|
119
164
|
}
|
|
@@ -144,8 +189,8 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
144
189
|
authToken?: string | null | undefined;
|
|
145
190
|
/**
|
|
146
191
|
* Override the default base URL. Defaults to
|
|
147
|
-
* `wss://
|
|
148
|
-
* URL for self-hosted or
|
|
192
|
+
* `wss://api.abloatai.com` for hosted production; pass an explicit
|
|
193
|
+
* URL for self-hosted or private deployments.
|
|
149
194
|
*/
|
|
150
195
|
baseURL?: string | null | undefined;
|
|
151
196
|
/**
|
|
@@ -168,7 +213,7 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
168
213
|
* Client-side use of this SDK is disabled by default — your apiKey
|
|
169
214
|
* would ship to every visitor's network tab. Only set this to
|
|
170
215
|
* `true` if you've understood the risk and have appropriate
|
|
171
|
-
* mitigations (a
|
|
216
|
+
* mitigations (a minted session token, a server-side proxy, etc).
|
|
172
217
|
*/
|
|
173
218
|
dangerouslyAllowBrowser?: boolean | undefined;
|
|
174
219
|
/**
|
|
@@ -179,6 +224,18 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
179
224
|
* only for the advanced Model / Claim / Commit client.
|
|
180
225
|
*/
|
|
181
226
|
schema: Schema<S>;
|
|
227
|
+
/**
|
|
228
|
+
* Short-lived-bearer resolver for the per-user browser path (mirrors the
|
|
229
|
+
* public {@link AbloOptions.getToken}). The client mints the first token
|
|
230
|
+
* before connect and refreshes it (timer + wake/online/focus) — see
|
|
231
|
+
* {@link resolveCredentialResolver}.
|
|
232
|
+
*/
|
|
233
|
+
getToken?: (() => Promise<string | null>) | undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Backend URL returning `{ token }`; sugar over {@link getToken}. Mirrors the
|
|
236
|
+
* public {@link AbloOptions.authEndpoint}.
|
|
237
|
+
*/
|
|
238
|
+
authEndpoint?: string | undefined;
|
|
182
239
|
/**
|
|
183
240
|
* @deprecated Server derives participant kind from the apiKey's
|
|
184
241
|
* scope. Pass apiKey only; this option will be removed once the
|
|
@@ -274,8 +331,8 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
274
331
|
* `commit` method against `${url}/graphql`) with one that uses your own
|
|
275
332
|
* GraphQL client, auth headers, retry policy, and observability hooks.
|
|
276
333
|
*
|
|
277
|
-
* Default: a fetch-based executor that targets `${url}/graphql`
|
|
278
|
-
*
|
|
334
|
+
* Default: a fetch-based executor that targets `${url}/graphql` and sends
|
|
335
|
+
* the configured bearer (`apiKey` / backend-minted token) as `Authorization`.
|
|
279
336
|
*/
|
|
280
337
|
mutationExecutor?: MutationExecutor;
|
|
281
338
|
/**
|
|
@@ -321,18 +378,14 @@ export interface InternalAbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
321
378
|
* Operations available on each model in the sync engine.
|
|
322
379
|
*
|
|
323
380
|
* Naming aligns with Stripe / OpenAI / Anthropic conventions:
|
|
324
|
-
* `retrieve(id)` — single
|
|
325
|
-
* `list({where})` — collection
|
|
326
|
-
* `
|
|
327
|
-
* `
|
|
328
|
-
* `
|
|
329
|
-
*
|
|
330
|
-
* The old verb set (`findById`, `findMany`, `findFirst`) is kept as
|
|
331
|
-
* deprecated aliases for one release cycle so consumers can migrate
|
|
332
|
-
* without a flag day.
|
|
381
|
+
* `retrieve({ id })` — async single-row server read
|
|
382
|
+
* `list({ where })` — async collection server read
|
|
383
|
+
* `get(id)` / `getAll(...)` / `getCount(...)` — local graph snapshots
|
|
384
|
+
* `create({ data })` / `update({ id, data })` / `delete({ id })` — writes
|
|
385
|
+
* `claim({ id })` — durable claim handle for coordinated writes
|
|
333
386
|
*/
|
|
334
|
-
export type { ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ClaimOptions,
|
|
335
|
-
import type { ModelOperations } from './createModelProxy.js';
|
|
387
|
+
export type { ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelRetrieveParams, ModelCreateParams, ModelUpdateParams, ModelDeleteParams, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, ClaimHandle, ModelOperations, } from './createModelProxy.js';
|
|
388
|
+
import type { ModelOperations, ClaimOptions, ClaimParams, ClaimLookupParams, ClaimReorderParams, ClaimHandle, ModelLoadOptions } from './createModelProxy.js';
|
|
336
389
|
export type ModelOperationAction = 'create' | 'update' | 'delete' | 'archive' | 'unarchive';
|
|
337
390
|
export type CommitWait = 'queued' | 'confirmed';
|
|
338
391
|
export interface ModelTarget {
|
|
@@ -349,6 +402,7 @@ export interface ModelClaim {
|
|
|
349
402
|
readonly actor: string;
|
|
350
403
|
readonly participantKind: ActiveIntent['participantKind'];
|
|
351
404
|
readonly action: string;
|
|
405
|
+
readonly description?: string;
|
|
352
406
|
readonly field?: string;
|
|
353
407
|
readonly status?: 'active' | 'queued';
|
|
354
408
|
readonly position?: number;
|
|
@@ -450,14 +504,130 @@ export interface ModelMutationOptions extends ClaimedOptions {
|
|
|
450
504
|
readonly readAt?: number | null;
|
|
451
505
|
readonly onStale?: 'reject' | 'force' | 'flag' | 'merge' | null;
|
|
452
506
|
readonly wait?: CommitWait;
|
|
507
|
+
readonly claim?: ClaimHandle | ClaimOptions | null;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* The HTTP/stateless claim surface. Normal tools usually put `claim` directly
|
|
511
|
+
* on the write (`update({ id, data, claim })`) and let the SDK release it. Use
|
|
512
|
+
* this namespace for multi-step handles and coordination screens.
|
|
513
|
+
*/
|
|
514
|
+
export interface HttpClaimApi<T> {
|
|
515
|
+
/** Take a manual claim handle for multi-step work. Release it when done. */
|
|
516
|
+
(params: ClaimParams<T>): Promise<ClaimHandle<T>>;
|
|
517
|
+
/** Release a manual claim you hold. */
|
|
518
|
+
release(params: ClaimLookupParams<T> | ClaimHandle<T>): Promise<void>;
|
|
519
|
+
/**
|
|
520
|
+
* Current holder of the lease on a row, or `null` when free. For UI badges,
|
|
521
|
+
* preflight checks, and operators.
|
|
522
|
+
*/
|
|
523
|
+
state(params: ClaimLookupParams<T>): Promise<Intent | null>;
|
|
524
|
+
/**
|
|
525
|
+
* FIFO wait line behind the holder. Advanced: useful for operator UIs and
|
|
526
|
+
* schedulers.
|
|
527
|
+
*/
|
|
528
|
+
queue(params: ClaimLookupParams<T>): Promise<{
|
|
529
|
+
readonly object: 'list';
|
|
530
|
+
readonly data: readonly Intent[];
|
|
531
|
+
}>;
|
|
532
|
+
/**
|
|
533
|
+
* Re-rank the wait line. Advanced and permission-gated.
|
|
534
|
+
*/
|
|
535
|
+
reorder(params: ClaimReorderParams<T>): Promise<void>;
|
|
453
536
|
}
|
|
454
537
|
export interface ModelClient<T = Record<string, unknown>> {
|
|
455
|
-
retrieve(
|
|
456
|
-
|
|
538
|
+
retrieve(params: ModelReadOptions & {
|
|
539
|
+
readonly id: string;
|
|
540
|
+
}): Promise<ModelRead<T>>;
|
|
541
|
+
/**
|
|
542
|
+
* Collection read over HTTP (server round-trip). Equality `where`, `orderBy`,
|
|
543
|
+
* `limit`. Present on the stateless protocol client; the store-backed
|
|
544
|
+
* `.model(name)` accessor omits it (use the typed `ablo.<model>.list` there).
|
|
545
|
+
*/
|
|
546
|
+
list?(options?: ModelLoadOptions<T>): Promise<T[]>;
|
|
547
|
+
create(params: ModelMutationOptions & {
|
|
548
|
+
readonly data: Record<string, unknown>;
|
|
457
549
|
readonly id?: string | null;
|
|
458
550
|
}): Promise<CommitReceipt>;
|
|
459
|
-
update(
|
|
460
|
-
|
|
551
|
+
update(params: ModelMutationOptions & {
|
|
552
|
+
readonly id: string;
|
|
553
|
+
readonly data: Record<string, unknown>;
|
|
554
|
+
}): Promise<CommitReceipt>;
|
|
555
|
+
delete(params: ModelMutationOptions & {
|
|
556
|
+
readonly id: string;
|
|
557
|
+
}): Promise<CommitReceipt>;
|
|
558
|
+
/**
|
|
559
|
+
* Durable lease + FIFO wait-line over HTTP — coordination without a socket.
|
|
560
|
+
* Present on the stateless protocol client (`Ablo({ schema: null })` /
|
|
561
|
+
* `createAbloHttpClient`); the store-backed `.model(name)` accessor omits it
|
|
562
|
+
* (the typed `ablo.<model>.claim` proxy is the full reactive namespace there).
|
|
563
|
+
*/
|
|
564
|
+
claim?: HttpClaimApi<T>;
|
|
565
|
+
}
|
|
566
|
+
/** A single data operation a scoped **agent** session may perform on a model. */
|
|
567
|
+
export type SessionOperation = 'read' | 'create' | 'update' | 'delete';
|
|
568
|
+
/** Mint params for an **end-user** session — full data authority within the
|
|
569
|
+
* org (the Stripe `ephemeralKeys.create` / Supabase session shape). Mints an
|
|
570
|
+
* `ek_` token. `user.id` is your end user's external IdP id (becomes the
|
|
571
|
+
* session's `participantId`); Ablo does not model your users, so it's an
|
|
572
|
+
* honest string at the trust boundary. */
|
|
573
|
+
export interface CreateUserSessionParams {
|
|
574
|
+
/** Your end user. `id` becomes the token's `participantId`. */
|
|
575
|
+
user: {
|
|
576
|
+
id: string;
|
|
577
|
+
};
|
|
578
|
+
/** Sync groups this session may subscribe to. Omit to inherit the key's scope. */
|
|
579
|
+
syncGroups?: readonly string[];
|
|
580
|
+
/** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
|
|
581
|
+
ttlSeconds?: number;
|
|
582
|
+
/** Opaque identity blob echoed back to the client as `ablo.user`. */
|
|
583
|
+
userMeta?: Record<string, unknown>;
|
|
584
|
+
agent?: never;
|
|
585
|
+
can?: never;
|
|
586
|
+
}
|
|
587
|
+
/** Mint params for a scoped **agent** session — mints a restricted `rk_` token
|
|
588
|
+
* gated to exactly the operations named in `can`. `can` is typed off your
|
|
589
|
+
* schema (no magic `'task.update'` strings): `{ Task: ['update'], Deck: ['read'] }`
|
|
590
|
+
* — the SDK serializes each entry to the wire allowlist (`task.update`). */
|
|
591
|
+
export interface CreateAgentSessionParams<S extends SchemaRecord> {
|
|
592
|
+
/** Your agent. `id` becomes the token's `participantId`. */
|
|
593
|
+
agent: {
|
|
594
|
+
id: string;
|
|
595
|
+
};
|
|
596
|
+
/** Per-model operation allowlist, typed against the schema's model names. */
|
|
597
|
+
can: {
|
|
598
|
+
[M in keyof S & string]?: readonly SessionOperation[];
|
|
599
|
+
};
|
|
600
|
+
/** Sync groups this session may subscribe to. Omit to inherit the key's scope. */
|
|
601
|
+
syncGroups?: readonly string[];
|
|
602
|
+
/** Token lifetime in seconds. Defaults to 900 (15m, the Stripe ephemeral default). */
|
|
603
|
+
ttlSeconds?: number;
|
|
604
|
+
/** Opaque identity blob echoed back to the client as `ablo.agent`. */
|
|
605
|
+
userMeta?: Record<string, unknown>;
|
|
606
|
+
user?: never;
|
|
607
|
+
}
|
|
608
|
+
/** Params for {@link Ablo.sessions}.create — a discriminated union: pass
|
|
609
|
+
* `{ user }` for a full-authority end-user session (`ek_`) or `{ agent, can }`
|
|
610
|
+
* for a scoped agent session (`rk_`). */
|
|
611
|
+
export type CreateSessionParams<S extends SchemaRecord> = CreateUserSessionParams | CreateAgentSessionParams<S>;
|
|
612
|
+
/** A minted end-user session token — the Stripe ephemeral-key / Supabase
|
|
613
|
+
* session resource. `token` is the secret the browser presents as its bearer. */
|
|
614
|
+
export interface AbloSession {
|
|
615
|
+
object: 'session';
|
|
616
|
+
/** Stable id of the minted credential (for revocation). */
|
|
617
|
+
id: string;
|
|
618
|
+
/** The short-lived `rk_` session token. Hand this to the user's browser. */
|
|
619
|
+
token: string;
|
|
620
|
+
/** ISO-8601 expiry. */
|
|
621
|
+
expiresAt: string;
|
|
622
|
+
organizationId: string;
|
|
623
|
+
scope: {
|
|
624
|
+
organizationId: string;
|
|
625
|
+
syncGroups: readonly string[];
|
|
626
|
+
operations: readonly string[];
|
|
627
|
+
participantKind: 'user' | 'agent' | 'system';
|
|
628
|
+
participantId: string;
|
|
629
|
+
};
|
|
630
|
+
userMeta: Record<string, unknown>;
|
|
461
631
|
}
|
|
462
632
|
/** The typed sync engine client — one property per model in the schema */
|
|
463
633
|
export type Ablo<S extends SchemaRecord> = {
|
|
@@ -491,8 +661,8 @@ export type Ablo<S extends SchemaRecord> = {
|
|
|
491
661
|
* offline, this waits until reconnect + flush completes.
|
|
492
662
|
*
|
|
493
663
|
* ```ts
|
|
494
|
-
* await sync.reports.create({ title: 'A' });
|
|
495
|
-
* await sync.reports.create({ title: 'B' });
|
|
664
|
+
* await sync.reports.create({ data: { title: 'A' } });
|
|
665
|
+
* await sync.reports.create({ data: { title: 'B' } });
|
|
496
666
|
* await sync.waitForFlush(); // server has both reports
|
|
497
667
|
* ```
|
|
498
668
|
*
|
|
@@ -502,6 +672,59 @@ export type Ablo<S extends SchemaRecord> = {
|
|
|
502
672
|
waitForFlush(timeoutMs?: number): Promise<void>;
|
|
503
673
|
/** Disconnect and clean up */
|
|
504
674
|
dispose(): Promise<void>;
|
|
675
|
+
/**
|
|
676
|
+
* Replace the bearer auth token used for the WebSocket upgrade and HTTP
|
|
677
|
+
* requests, WITHOUT tearing down the engine. Use to push a refreshed
|
|
678
|
+
* short-lived access key (the Stripe-style `ek_`/`rk_`) before it expires —
|
|
679
|
+
* `<AbloProvider>`'s `getToken` refresh loop calls this. Reuses the same
|
|
680
|
+
* rotation path as the internal capability-token refresh; safe to call before
|
|
681
|
+
* `ready()`. Also nudges a parked connection to re-probe with the new token.
|
|
682
|
+
*/
|
|
683
|
+
setAuthToken(token: string): void;
|
|
684
|
+
/**
|
|
685
|
+
* Resolve the active bearer credential this engine authenticates with — the
|
|
686
|
+
* live `ek_`/`rk_` the WebSocket and HTTP transports currently carry (kept
|
|
687
|
+
* fresh by the `getToken` refresh loop), falling back to a configured API
|
|
688
|
+
* key. Returns `null` when no credential is set yet. Use it to authenticate
|
|
689
|
+
* a side-band request to the same sync-server (e.g. the S3 presign endpoint)
|
|
690
|
+
* with the very token this client already holds — no extra mint round-trip.
|
|
691
|
+
*/
|
|
692
|
+
getAuthToken(): Promise<string | null>;
|
|
693
|
+
/**
|
|
694
|
+
* Register a re-mint hook for the short-lived access key. The connection
|
|
695
|
+
* layer calls it WHEN it finds the key stale (a `credential_stale` probe) or
|
|
696
|
+
* on an external nudge; the hook mints a fresh `ek_`/`rk_` from the still-valid
|
|
697
|
+
* login. Mirrors the `getToken` contract: resolve a token, resolve `null` when
|
|
698
|
+
* the login itself is gone (→ sign out), or THROW on a transient failure (→
|
|
699
|
+
* back off, never sign out). `<AbloProvider>` wires this from its
|
|
700
|
+
* `getToken`/`authEndpoint`. Safe to call before `ready()`.
|
|
701
|
+
*/
|
|
702
|
+
setCredentialRefresher(refresher: (() => Promise<string | null>) | null): void;
|
|
703
|
+
/**
|
|
704
|
+
* Ask the connection layer to re-probe and reconnect now, using the current
|
|
705
|
+
* credential. Idempotent and safe in any state (a no-op while connected).
|
|
706
|
+
* Call after an OS-wake signal (Electron `powerMonitor` 'resume') so a
|
|
707
|
+
* connection parked since sleep recovers immediately instead of waiting for
|
|
708
|
+
* the watchdog.
|
|
709
|
+
*/
|
|
710
|
+
nudgeReconnect(): void;
|
|
711
|
+
/**
|
|
712
|
+
* Mint a short-lived, scoped **session token** for one end user — the
|
|
713
|
+
* Stripe `ephemeralKeys.create` / Supabase session shape. Call this on YOUR
|
|
714
|
+
* BACKEND (where the `sk_` secret key lives), then hand the returned
|
|
715
|
+
* `token` to that user's browser (typically via an authEndpoint the client
|
|
716
|
+
* fetches). The browser presents it as the bearer; the sync-server verifies
|
|
717
|
+
* the scoped `rk_` token via `apiKeyProvider`.
|
|
718
|
+
*
|
|
719
|
+
* The browser must NEVER see the `sk_` key — only the per-user session token.
|
|
720
|
+
*
|
|
721
|
+
* Pass `{ user: { id } }` for a full-authority end-user session (mints `ek_`),
|
|
722
|
+
* or `{ agent: { id }, can: { Task: ['update'] } }` for a scoped agent
|
|
723
|
+
* session (mints `rk_`); `can` is typed against your schema's model names.
|
|
724
|
+
*/
|
|
725
|
+
sessions: {
|
|
726
|
+
create(params: CreateSessionParams<S>): Promise<AbloSession>;
|
|
727
|
+
};
|
|
505
728
|
/**
|
|
506
729
|
* Destroy every IndexedDB database owned by this engine. Disconnects
|
|
507
730
|
* the WebSocket, releases timers, and deletes all `ablo_*` / `ablo-*`
|
|
@@ -766,6 +989,8 @@ export declare namespace Ablo {
|
|
|
766
989
|
type CapabilityRecord = import('./ApiClient.js').CapabilityRecord;
|
|
767
990
|
type CapabilityResource = import('./ApiClient.js').CapabilityResource;
|
|
768
991
|
type CapabilityRevocation = import('./ApiClient.js').CapabilityRevocation;
|
|
992
|
+
type CapabilityRotateOptions = import('./ApiClient.js').CapabilityRotateOptions;
|
|
993
|
+
type RotatedCapability = import('./ApiClient.js').RotatedCapability;
|
|
769
994
|
type Task = import('./ApiClient.js').Task;
|
|
770
995
|
type TaskCreateOptions = import('./ApiClient.js').TaskCreateOptions;
|
|
771
996
|
type TaskCloseOptions = import('./ApiClient.js').TaskCloseOptions;
|
|
@@ -838,6 +1063,7 @@ export declare namespace Ablo {
|
|
|
838
1063
|
namespace Source {
|
|
839
1064
|
type Operation = import('../source/index.js').SourceOperation;
|
|
840
1065
|
type Event = import('../source/index.js').SourceEvent;
|
|
1066
|
+
type EventForOperationOptions = import('../source/index.js').SourceEventForOperationOptions;
|
|
841
1067
|
type EventsResult = import('../source/index.js').SourceEventsResult;
|
|
842
1068
|
type Scope = import('../source/index.js').SourceScope;
|
|
843
1069
|
type ApiKey = import('../source/index.js').SourceApiKey;
|