@abloatai/ablo 0.3.1 → 0.4.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 +39 -1
- package/NOTICE +2 -2
- package/README.md +27 -25
- package/dist/agent/Agent.d.ts +1 -1
- package/dist/agent/Agent.js +1 -1
- package/dist/agent/index.d.ts +4 -4
- package/dist/agent/index.js +6 -6
- package/dist/agent/types.d.ts +1 -1
- package/dist/ai-sdk/index.d.ts +3 -3
- package/dist/ai-sdk/index.js +3 -3
- package/dist/ai-sdk/intent-broadcast.d.ts +1 -1
- package/dist/ai-sdk/intent-broadcast.js +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/client/Ablo.d.ts +8 -14
- package/dist/client/Ablo.js +32 -1
- package/dist/client/auth.d.ts +3 -3
- package/dist/client/auth.js +5 -5
- package/dist/client/createModelProxy.d.ts +110 -32
- package/dist/client/createModelProxy.js +77 -38
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +2 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +9 -9
- package/dist/interfaces/headless.d.ts +1 -1
- package/dist/interfaces/headless.js +2 -2
- package/dist/policy/index.d.ts +2 -2
- package/dist/policy/index.js +2 -2
- package/dist/principal.d.ts +1 -1
- package/dist/principal.js +1 -1
- package/dist/react/ClientSideSuspense.d.ts +1 -1
- package/dist/react/SyncGroupProvider.js +1 -1
- package/dist/react/context.d.ts +1 -1
- package/dist/react/context.js +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/useCurrentUserId.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutate.d.ts +1 -1
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react/useReader.d.ts +1 -1
- package/dist/schema/field.d.ts +1 -1
- package/dist/schema/field.js +1 -1
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +2 -2
- package/dist/schema/model.d.ts +2 -2
- package/dist/schema/model.js +2 -2
- package/dist/schema/queries.d.ts +1 -1
- package/dist/schema/queries.js +1 -1
- package/dist/schema/relation.d.ts +1 -1
- package/dist/schema/relation.js +1 -1
- package/dist/schema/schema.d.ts +1 -1
- package/dist/schema/schema.js +1 -1
- package/dist/source/index.d.ts +22 -28
- package/dist/source/index.js +23 -20
- package/dist/source/pushQueue.d.ts +1 -1
- package/dist/source/pushQueue.js +2 -2
- package/dist/sync/SyncWebSocket.d.ts +14 -0
- package/dist/sync/createIntentStream.js +7 -0
- package/dist/testing/fixtures/models.d.ts +1 -1
- package/dist/testing/fixtures/models.js +1 -1
- package/dist/testing/helpers/react-wrapper.d.ts +2 -2
- package/dist/testing/helpers/react-wrapper.js +2 -2
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +1 -1
- package/dist/types/streams.d.ts +39 -0
- package/docs/api.md +78 -20
- package/docs/data-sources.md +50 -16
- package/docs/examples/ai-sdk-tool.md +14 -31
- package/docs/examples/existing-python-backend.md +6 -6
- package/docs/integration-guide.md +8 -7
- package/docs/interaction-model.md +16 -4
- package/docs/mcp.md +1 -1
- package/docs/quickstart.md +20 -18
- package/examples/data-source/README.md +1 -1
- package/examples/data-source/ablo-driver.ts +5 -5
- package/examples/data-source/customer-server.ts +10 -10
- package/examples/data-source/run.ts +9 -11
- package/examples/data-source/schema.ts +1 -1
- package/examples/quickstart.ts +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Each schema model gets one `ModelOperations<T, CreateInput>` —
|
|
10
10
|
* exposes `retrieve`, `list`, `count`, `create`, `update`, `delete`,
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `intent` (the coordination handle), `subscribe`, and `load`. The
|
|
12
|
+
* factory returns a plain object; the client assembles the
|
|
13
|
+
* `ablo.<model>` lookup table from these.
|
|
14
14
|
*/
|
|
15
15
|
import type { MutationOptions } from '../interfaces/index.js';
|
|
16
16
|
import type { ModelRegistry } from '../ModelRegistry.js';
|
|
@@ -19,7 +19,7 @@ import type { SyncClient } from '../SyncClient.js';
|
|
|
19
19
|
import type { HydrationCoordinator } from '../sync/HydrationCoordinator.js';
|
|
20
20
|
import type { LoadWhere } from '../query/types.js';
|
|
21
21
|
import { ModelScope } from '../types/index.js';
|
|
22
|
-
import type { Duration, Snapshot } from '../types/streams.js';
|
|
22
|
+
import type { Duration, Intent, IntentStatus, IntentWaitOptions, Snapshot } from '../types/streams.js';
|
|
23
23
|
export interface ModelResourceMeta {
|
|
24
24
|
readonly key: string;
|
|
25
25
|
readonly typename: string;
|
|
@@ -66,30 +66,7 @@ export interface ModelLoadOptions<T> {
|
|
|
66
66
|
*/
|
|
67
67
|
expand?: readonly string[];
|
|
68
68
|
}
|
|
69
|
-
export interface
|
|
70
|
-
/**
|
|
71
|
-
* Human-readable activity shown to other participants while this handle
|
|
72
|
-
* is open. Examples: `editing`, `summarizing`, `rewriting`, `reviewing`.
|
|
73
|
-
*/
|
|
74
|
-
activity?: string;
|
|
75
|
-
/** Optional field-level target for UI affordances such as busy badges. */
|
|
76
|
-
field?: keyof T & string;
|
|
77
|
-
/** Lease duration for the visible activity. Runtime death is cleaned up by TTL. */
|
|
78
|
-
ttl?: Duration;
|
|
79
|
-
/** Default wait mode for `handle.update(...)`. Defaults to `confirmed`. */
|
|
80
|
-
wait?: MutationOptions['wait'];
|
|
81
|
-
}
|
|
82
|
-
export interface ModelEditHandle<T> extends AsyncDisposable {
|
|
83
|
-
readonly id: string;
|
|
84
|
-
readonly intentId: string;
|
|
85
|
-
readonly activity: string;
|
|
86
|
-
readonly current: T;
|
|
87
|
-
readonly signal: AbortSignal;
|
|
88
|
-
update(data: Partial<T>, options?: MutationOptions): Promise<T>;
|
|
89
|
-
release(): Promise<void>;
|
|
90
|
-
revoke(): void;
|
|
91
|
-
}
|
|
92
|
-
export interface ModelIntentHandle {
|
|
69
|
+
export interface IntentLeaseHandle {
|
|
93
70
|
readonly id: string;
|
|
94
71
|
release(): Promise<void>;
|
|
95
72
|
revoke(): void;
|
|
@@ -103,8 +80,99 @@ export interface ModelCollaboration<T> {
|
|
|
103
80
|
};
|
|
104
81
|
action: string;
|
|
105
82
|
ttl?: Duration;
|
|
106
|
-
}): Promise<
|
|
83
|
+
}): Promise<IntentLeaseHandle>;
|
|
107
84
|
createSnapshot(modelKey: string, id: string): Snapshot;
|
|
85
|
+
/**
|
|
86
|
+
* Current coordination state on a target — who (if anyone) holds it.
|
|
87
|
+
* Synchronous reactive snapshot read off the presence/intent stream;
|
|
88
|
+
* `null` when the target is free. The wiring site computes it because
|
|
89
|
+
* only it knows the local participant id (needed to distinguish "I
|
|
90
|
+
* hold it" from "someone else holds it").
|
|
91
|
+
*/
|
|
92
|
+
observe(target: {
|
|
93
|
+
resource: string;
|
|
94
|
+
id: string;
|
|
95
|
+
}): Intent | null;
|
|
96
|
+
/**
|
|
97
|
+
* Resolve once no participant holds an active intent on the target.
|
|
98
|
+
* The contender's "wait until it's free" — delegates to the intent
|
|
99
|
+
* stream's `waitFor`.
|
|
100
|
+
*/
|
|
101
|
+
waitFor(target: {
|
|
102
|
+
resource: string;
|
|
103
|
+
id: string;
|
|
104
|
+
}, options?: IntentWaitOptions): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* The local participant's id. Used to distinguish "I already hold this"
|
|
107
|
+
* from "someone else holds it" in `acquireOrAwait`.
|
|
108
|
+
*/
|
|
109
|
+
readonly selfParticipantId: string;
|
|
110
|
+
}
|
|
111
|
+
/** Options for acquiring a per-model coordination intent. */
|
|
112
|
+
export interface ModelIntentAcquireOptions {
|
|
113
|
+
/** Phase shown to others while held. Defaults to `'editing'`. */
|
|
114
|
+
action?: string;
|
|
115
|
+
/** Field-level target for busy badges. */
|
|
116
|
+
field?: string;
|
|
117
|
+
/** Lease duration; runtime death is cleaned up by TTL. */
|
|
118
|
+
ttl?: Duration;
|
|
119
|
+
/** Default wait mode for `handle.update(...)`. Defaults to `confirmed`. */
|
|
120
|
+
wait?: MutationOptions['wait'];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Per-entity coordination handle — the same accessor shape as
|
|
124
|
+
* `create`/`update`/`retrieve`, but on the coordination plane. Returned
|
|
125
|
+
* synchronously by `ablo.<model>.intent(id)` so a contender can read
|
|
126
|
+
* `.current` without awaiting; `acquire()` is the async lock.
|
|
127
|
+
*
|
|
128
|
+
* Read side (any participant): `current`, `status`, `settled()`.
|
|
129
|
+
* Write side (the holder): `acquire()`, `update()`, `release()`.
|
|
130
|
+
*/
|
|
131
|
+
export interface ModelIntentHandle<T> extends AsyncDisposable {
|
|
132
|
+
/** The target entity id this handle coordinates. */
|
|
133
|
+
readonly id: string;
|
|
134
|
+
/**
|
|
135
|
+
* Live coordination state on this target — `null` when free, otherwise
|
|
136
|
+
* the holder's `Intent` (who, what phase, until when). Reactive
|
|
137
|
+
* snapshot; pair with the model's `subscribe` for change notifications.
|
|
138
|
+
*/
|
|
139
|
+
readonly current: Intent | null;
|
|
140
|
+
/** Convenience: `current?.status ?? 'idle'`. */
|
|
141
|
+
readonly status: IntentStatus | 'idle';
|
|
142
|
+
/**
|
|
143
|
+
* Acquire the lease so other participants yield. Resolves once the
|
|
144
|
+
* claim is announced. Throws if another participant already holds it
|
|
145
|
+
* (cooperative mutex enforced at the server boundary).
|
|
146
|
+
*/
|
|
147
|
+
acquire(options?: ModelIntentAcquireOptions): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* Acquire the target, or — if another participant holds it — wait for
|
|
150
|
+
* them to finish, re-read the (now-changed) row, then acquire. This is
|
|
151
|
+
* the runtime's serialize-on-contention primitive: the caller never
|
|
152
|
+
* branches on who holds the target, it just gets the target safely.
|
|
153
|
+
*
|
|
154
|
+
* A claim held by *this* participant is treated as already-mine and
|
|
155
|
+
* acquired without waiting. Bind this to an agent's write-tool boundary
|
|
156
|
+
* so agents never reason about coordination themselves.
|
|
157
|
+
*/
|
|
158
|
+
acquireOrAwait(options?: ModelIntentAcquireOptions): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Optimistic update guarded by the lease this handle holds. Rejects
|
|
161
|
+
* with `AbloStaleContextError` if the row changed under you, then
|
|
162
|
+
* auto-releases. Call `acquire()` first.
|
|
163
|
+
*/
|
|
164
|
+
update(data: Partial<T>, options?: MutationOptions): Promise<T>;
|
|
165
|
+
/** Release a lease you hold (commit / abandon). */
|
|
166
|
+
release(): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Wait until the target is free, then resolve. The contender's
|
|
169
|
+
* "let me wait until it completes." On resolution your cached copy may
|
|
170
|
+
* be stale — re-read before writing (the stale-context guard enforces
|
|
171
|
+
* this if you go through `acquire().update()`).
|
|
172
|
+
*/
|
|
173
|
+
settled(options?: IntentWaitOptions): Promise<void>;
|
|
174
|
+
/** Drop a held lease without committing. */
|
|
175
|
+
revoke(): void;
|
|
108
176
|
}
|
|
109
177
|
export interface ModelOperations<T, CreateInput> {
|
|
110
178
|
/**
|
|
@@ -135,10 +203,20 @@ export interface ModelOperations<T, CreateInput> {
|
|
|
135
203
|
/** Delete an entity by id — optimistic, offline-first (see `create`). */
|
|
136
204
|
delete(id: string, options?: MutationOptions): Promise<void>;
|
|
137
205
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
206
|
+
* Coordination accessor for one entity — the same `ablo.<model>(id)`
|
|
207
|
+
* shape as `create`/`update`/`retrieve`, but on the coordination plane
|
|
208
|
+
* (ephemeral, TTL'd, never persisted). Returns a handle synchronously:
|
|
209
|
+
* read `.current` to see who's editing, `acquire()` to lock, `update()`
|
|
210
|
+
* to write under the lock, `settled()` to wait for a holder to finish.
|
|
211
|
+
*
|
|
212
|
+
* ```ts
|
|
213
|
+
* const lock = ablo.slide.intent(slideId);
|
|
214
|
+
* if (lock.current) await lock.settled(); // someone's editing — wait
|
|
215
|
+
* await lock.acquire({ action: 'editing' });
|
|
216
|
+
* await lock.update({ title: 'New' }); // auto-releases
|
|
217
|
+
* ```
|
|
140
218
|
*/
|
|
141
|
-
|
|
219
|
+
intent(id: string): ModelIntentHandle<T>;
|
|
142
220
|
/** Subscribe to changes (callback called on every change). */
|
|
143
221
|
subscribe(callback: (entities: T[]) => void, options?: ModelListOptions<T>): () => void;
|
|
144
222
|
/**
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Each schema model gets one `ModelOperations<T, CreateInput>` —
|
|
10
10
|
* exposes `retrieve`, `list`, `count`, `create`, `update`, `delete`,
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `intent` (the coordination handle), `subscribe`, and `load`. The
|
|
12
|
+
* factory returns a plain object; the client assembles the
|
|
13
|
+
* `ablo.<model>` lookup table from these.
|
|
14
14
|
*/
|
|
15
15
|
import { autorun } from 'mobx';
|
|
16
16
|
import { AbloStaleContextError, AbloValidationError } from '../errors.js';
|
|
@@ -111,54 +111,92 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
111
111
|
syncClient.delete(model, options);
|
|
112
112
|
await waitForMutation(model, options);
|
|
113
113
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
let model = objectPool.get(id);
|
|
119
|
-
if (!model) {
|
|
120
|
-
await load({ where: { id } });
|
|
121
|
-
model = objectPool.get(id);
|
|
122
|
-
}
|
|
123
|
-
if (!model) {
|
|
124
|
-
throw new AbloValidationError(`Entity not found: ${registeredModelName}/${id}`, { code: 'entity_not_found' });
|
|
125
|
-
}
|
|
126
|
-
const activity = options?.activity ?? 'editing';
|
|
127
|
-
const snapshot = collaboration.createSnapshot(schemaKey, id);
|
|
128
|
-
const intent = await collaboration.createIntent({
|
|
129
|
-
target: {
|
|
130
|
-
resource: schemaKey,
|
|
131
|
-
id,
|
|
132
|
-
...(options?.field ? { field: options.field } : {}),
|
|
133
|
-
},
|
|
134
|
-
action: activity,
|
|
135
|
-
ttl: options?.ttl,
|
|
136
|
-
});
|
|
114
|
+
intent(id) {
|
|
115
|
+
const target = { resource: schemaKey, id };
|
|
116
|
+
let acquired = null;
|
|
117
|
+
let snapshot = null;
|
|
137
118
|
let released = false;
|
|
119
|
+
let acquireWait;
|
|
138
120
|
const revoke = () => {
|
|
139
121
|
if (released)
|
|
140
122
|
return;
|
|
141
123
|
released = true;
|
|
142
|
-
snapshot
|
|
143
|
-
|
|
124
|
+
if (snapshot)
|
|
125
|
+
snapshot.signal.removeEventListener('abort', revoke);
|
|
126
|
+
acquired?.revoke();
|
|
144
127
|
};
|
|
145
128
|
const release = async () => {
|
|
146
129
|
if (released)
|
|
147
130
|
return;
|
|
148
131
|
released = true;
|
|
149
|
-
snapshot
|
|
150
|
-
|
|
132
|
+
if (snapshot)
|
|
133
|
+
snapshot.signal.removeEventListener('abort', revoke);
|
|
134
|
+
await acquired?.release();
|
|
135
|
+
};
|
|
136
|
+
const settled = async (options) => {
|
|
137
|
+
if (!collaboration)
|
|
138
|
+
return;
|
|
139
|
+
await collaboration.waitFor(target, options);
|
|
140
|
+
};
|
|
141
|
+
const acquire = async (options) => {
|
|
142
|
+
if (!collaboration) {
|
|
143
|
+
throw new AbloValidationError(`Model "${schemaKey}" cannot acquire an intent without collaboration wiring.`, { code: 'model_intent_not_configured' });
|
|
144
|
+
}
|
|
145
|
+
if (acquired)
|
|
146
|
+
return;
|
|
147
|
+
acquireWait = options?.wait;
|
|
148
|
+
// Load the row so update() has a snapshot to guard against.
|
|
149
|
+
let model = objectPool.get(id);
|
|
150
|
+
if (!model) {
|
|
151
|
+
await load({ where: { id } });
|
|
152
|
+
model = objectPool.get(id);
|
|
153
|
+
}
|
|
154
|
+
if (!model) {
|
|
155
|
+
throw new AbloValidationError(`Entity not found: ${registeredModelName}/${id}`, { code: 'entity_not_found' });
|
|
156
|
+
}
|
|
157
|
+
const snap = collaboration.createSnapshot(schemaKey, id);
|
|
158
|
+
snap.signal.addEventListener('abort', revoke, { once: true });
|
|
159
|
+
snapshot = snap;
|
|
160
|
+
released = false;
|
|
161
|
+
acquired = await collaboration.createIntent({
|
|
162
|
+
target: {
|
|
163
|
+
resource: schemaKey,
|
|
164
|
+
id,
|
|
165
|
+
...(options?.field ? { field: options.field } : {}),
|
|
166
|
+
},
|
|
167
|
+
action: options?.action ?? 'editing',
|
|
168
|
+
ttl: options?.ttl,
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
const acquireOrAwait = async (options) => {
|
|
172
|
+
if (!collaboration) {
|
|
173
|
+
throw new AbloValidationError(`Model "${schemaKey}" cannot acquire an intent without collaboration wiring.`, { code: 'model_intent_not_configured' });
|
|
174
|
+
}
|
|
175
|
+
const held = collaboration.observe(target);
|
|
176
|
+
// A foreign holder: wait for release, then re-read before claiming.
|
|
177
|
+
// Our own claim (or a free target) skips straight to acquire.
|
|
178
|
+
if (held && held.heldBy !== collaboration.selfParticipantId) {
|
|
179
|
+
await settled();
|
|
180
|
+
await load({ where: { id } });
|
|
181
|
+
}
|
|
182
|
+
await acquire(options);
|
|
151
183
|
};
|
|
152
|
-
snapshot.signal.addEventListener('abort', revoke, { once: true });
|
|
153
184
|
const handle = {
|
|
154
185
|
id,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
186
|
+
get current() {
|
|
187
|
+
return collaboration?.observe(target) ?? null;
|
|
188
|
+
},
|
|
189
|
+
get status() {
|
|
190
|
+
return collaboration?.observe(target)?.status ?? 'idle';
|
|
191
|
+
},
|
|
192
|
+
acquire,
|
|
193
|
+
acquireOrAwait,
|
|
159
194
|
async update(data, updateOptions) {
|
|
195
|
+
if (!acquired || !snapshot) {
|
|
196
|
+
throw new AbloValidationError(`Call acquire() before update() on ablo.${schemaKey}.intent(${id}).`, { code: 'intent_not_acquired' });
|
|
197
|
+
}
|
|
160
198
|
if (snapshot.signal.aborted) {
|
|
161
|
-
throw new AbloStaleContextError(`
|
|
199
|
+
throw new AbloStaleContextError(`Intent context is stale for ${schemaKey}/${id}. Re-read the row and retry.`, {
|
|
162
200
|
code: 'edit_context_stale',
|
|
163
201
|
readAt: snapshot.stamp,
|
|
164
202
|
cause: snapshot.signal.reason,
|
|
@@ -166,11 +204,11 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
166
204
|
}
|
|
167
205
|
try {
|
|
168
206
|
return await operations.update(id, data, {
|
|
169
|
-
wait:
|
|
207
|
+
wait: acquireWait ?? 'confirmed',
|
|
170
208
|
readAt: snapshot.stamp,
|
|
171
209
|
onStale: 'reject',
|
|
172
210
|
...updateOptions,
|
|
173
|
-
intent,
|
|
211
|
+
intent: acquired,
|
|
174
212
|
});
|
|
175
213
|
}
|
|
176
214
|
finally {
|
|
@@ -178,6 +216,7 @@ export function createModelProxy(schemaKey, registeredModelName, objectPool, syn
|
|
|
178
216
|
}
|
|
179
217
|
},
|
|
180
218
|
release,
|
|
219
|
+
settled,
|
|
181
220
|
revoke,
|
|
182
221
|
[Symbol.asyncDispose]: release,
|
|
183
222
|
};
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/client — Consumer API
|
|
3
3
|
*
|
|
4
4
|
* The one-liner entry point for external consumers.
|
|
5
5
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* when you want the realtime sync engine with typed model proxies.
|
|
8
8
|
*
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { Ablo } from '@ablo/
|
|
10
|
+
* import { Ablo } from '@abloatai/ablo/client';
|
|
11
11
|
* import { schema } from './schema';
|
|
12
12
|
*
|
|
13
13
|
* const ablo = Ablo({
|
package/dist/client/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/client — Consumer API
|
|
3
3
|
*
|
|
4
4
|
* The one-liner entry point for external consumers.
|
|
5
5
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* when you want the realtime sync engine with typed model proxies.
|
|
8
8
|
*
|
|
9
9
|
* ```ts
|
|
10
|
-
* import { Ablo } from '@ablo/
|
|
10
|
+
* import { Ablo } from '@abloatai/ablo/client';
|
|
11
11
|
* import { schema } from './schema';
|
|
12
12
|
*
|
|
13
13
|
* const ablo = Ablo({
|
package/dist/config/index.d.ts
CHANGED
package/dist/config/index.js
CHANGED
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/core — Framework extension
|
|
3
3
|
*
|
|
4
4
|
* Only imported by SyncedStore.ts and ApplicationStore.ts —
|
|
5
5
|
* the 2-3 files that extend or orchestrate the sync engine.
|
|
@@ -13,7 +13,7 @@ export { Database } from '../Database.js';
|
|
|
13
13
|
export { ObjectPool, ModelScope } from '../ObjectPool.js';
|
|
14
14
|
export { Model } from '../Model.js';
|
|
15
15
|
export { LazyReferenceCollection } from '../LazyReferenceCollection.js';
|
|
16
|
-
// Undo runtime — `useUndoScope` hook from `@ablo/
|
|
16
|
+
// Undo runtime — `useUndoScope` hook from `@abloatai/ablo/react` is
|
|
17
17
|
// the canonical access path. Type counterparts (`Ablo.Mutator.UndoScope`,
|
|
18
18
|
// `Ablo.Mutator.UndoEntry`, `Ablo.Mutator.InverseOp`) live on the main `Ablo`
|
|
19
19
|
// namespace. Direct class access (tests, non-React hosts) imports via
|
package/dist/errors.d.ts
CHANGED
package/dist/errors.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo
|
|
2
|
+
* @abloatai/ablo — The Collaboration Layer for AI and Humans
|
|
3
3
|
*
|
|
4
4
|
* ```ts
|
|
5
|
-
* import Ablo from '@ablo
|
|
5
|
+
* import Ablo from '@abloatai/ablo';
|
|
6
6
|
*
|
|
7
7
|
* const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
8
8
|
* await ablo.tasks.load({ where: { id: 'task_123' } });
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
* reached via dot-access on the engine, types via namespace dots.
|
|
20
20
|
*
|
|
21
21
|
* Public subpaths:
|
|
22
|
-
* @ablo/
|
|
23
|
-
* @ablo/
|
|
24
|
-
* @ablo/
|
|
22
|
+
* @abloatai/ablo/schema — defineSchema, model, z (Zod)
|
|
23
|
+
* @abloatai/ablo/react — <AbloProvider>, useQuery, useMutate
|
|
24
|
+
* @abloatai/ablo/testing — test harnesses + mocks
|
|
25
25
|
*
|
|
26
26
|
* Consumer code should converge on `ablo.<model>.load(...)`, which routes
|
|
27
27
|
* through the engine's `HydrationCoordinator` and dedupes single-flight
|
|
28
28
|
* hydrations.
|
|
29
29
|
*/
|
|
30
30
|
export { Ablo } from './client/Ablo.js';
|
|
31
|
-
export type { AbloOptions, ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions,
|
|
31
|
+
export type { AbloOptions, ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelIntentHandle, ModelIntentAcquireOptions, ModelOperations, } from './client/Ablo.js';
|
|
32
32
|
export type { AbloPersistence } from './client/persistence.js';
|
|
33
33
|
export { session, agent } from './principal.js';
|
|
34
34
|
import { Ablo } from './client/Ablo.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo
|
|
2
|
+
* @abloatai/ablo — The Collaboration Layer for AI and Humans
|
|
3
3
|
*
|
|
4
4
|
* ```ts
|
|
5
|
-
* import Ablo from '@ablo
|
|
5
|
+
* import Ablo from '@abloatai/ablo';
|
|
6
6
|
*
|
|
7
7
|
* const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
8
8
|
* await ablo.tasks.load({ where: { id: 'task_123' } });
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
* reached via dot-access on the engine, types via namespace dots.
|
|
20
20
|
*
|
|
21
21
|
* Public subpaths:
|
|
22
|
-
* @ablo/
|
|
23
|
-
* @ablo/
|
|
24
|
-
* @ablo/
|
|
22
|
+
* @abloatai/ablo/schema — defineSchema, model, z (Zod)
|
|
23
|
+
* @abloatai/ablo/react — <AbloProvider>, useQuery, useMutate
|
|
24
|
+
* @abloatai/ablo/testing — test harnesses + mocks
|
|
25
25
|
*
|
|
26
26
|
* Consumer code should converge on `ablo.<model>.load(...)`, which routes
|
|
27
27
|
* through the engine's `HydrationCoordinator` and dedupes single-flight
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
// Everything else is in a subpath.
|
|
33
33
|
// The canonical surface — `Ablo` is a function, type, and namespace under
|
|
34
34
|
// one name. Matches `Stripe`, `OpenAI`, `Anthropic`. Default export so
|
|
35
|
-
// `import Ablo from '@ablo
|
|
35
|
+
// `import Ablo from '@abloatai/ablo'` works; named export so
|
|
36
36
|
// `import { Ablo }` also compiles.
|
|
37
37
|
export { Ablo } from './client/Ablo.js';
|
|
38
38
|
// Participant types live under `Ablo.Participant.*` —
|
|
@@ -50,9 +50,9 @@ export default Ablo;
|
|
|
50
50
|
// helpers ship flat; type counterparts live under `Ablo.Source.*`
|
|
51
51
|
// (`Ablo.Source.Operation`, `Ablo.Source.Commit.Params`, etc.).
|
|
52
52
|
export { dataSource, abloSource, signAbloSourceRequest, verifyAbloSourceRequest, } from './source/index.js';
|
|
53
|
-
// Schema DSL is intentionally published from `@ablo/
|
|
53
|
+
// Schema DSL is intentionally published from `@abloatai/ablo/schema`.
|
|
54
54
|
// Keeping it out of the root import preserves one clean runtime surface:
|
|
55
|
-
// `import Ablo from '@ablo
|
|
55
|
+
// `import Ablo from '@abloatai/ablo'`.
|
|
56
56
|
// Conflict policy — `defaultPolicy` (the rejecting default) is a value
|
|
57
57
|
// callers reference if they want to compose. The type counterparts
|
|
58
58
|
// (`Conflict`, `ConflictPolicy`, etc.) live under `Ablo.Conflict`,
|
|
@@ -79,4 +79,4 @@ export { defineMutators } from './mutators/defineMutators.js';
|
|
|
79
79
|
// pass it as `{ tx, args }` to the mutator function.
|
|
80
80
|
export { createTransaction } from './mutators/Transaction.js';
|
|
81
81
|
// Undo runtime is intentionally not part of the public root surface. App code
|
|
82
|
-
// uses `useUndoScope` from `@ablo/
|
|
82
|
+
// uses `useUndoScope` from `@abloatai/ablo/react`.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* const engine = createSyncEngine({ url, userId, organizationId });
|
|
13
13
|
*
|
|
14
14
|
* // Node.js / agent / sidecar (headless — DI overrides)
|
|
15
|
-
* import { inMemoryStorage, alwaysOnline } from '@ablo/
|
|
15
|
+
* import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
|
|
16
16
|
* const engine = createSyncEngine({
|
|
17
17
|
* url, userId, organizationId,
|
|
18
18
|
* storage: inMemoryStorage(),
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* const engine = createSyncEngine({ url, userId, organizationId });
|
|
13
13
|
*
|
|
14
14
|
* // Node.js / agent / sidecar (headless — DI overrides)
|
|
15
|
-
* import { inMemoryStorage, alwaysOnline } from '@ablo/
|
|
15
|
+
* import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
|
|
16
16
|
* const engine = createSyncEngine({
|
|
17
17
|
* url, userId, organizationId,
|
|
18
18
|
* storage: inMemoryStorage(),
|
|
@@ -32,7 +32,7 @@ export {};
|
|
|
32
32
|
//
|
|
33
33
|
// These will be the public API that headless consumers import:
|
|
34
34
|
//
|
|
35
|
-
// import { inMemoryStorage, alwaysOnline } from '@ablo/
|
|
35
|
+
// import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
|
|
36
36
|
//
|
|
37
37
|
// Stubs below show the intended signatures. Implementation is Phase 1 work.
|
|
38
38
|
// export function inMemoryStorage(): StorageProvider { ... }
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/policy — pluggable conflict resolution.
|
|
3
3
|
*
|
|
4
4
|
* The engine detects conflicts; the policy decides. Customer code
|
|
5
5
|
* implements `ConflictPolicy` and registers it at the sync-server.
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
|
-
* import { type ConflictPolicy, defaultPolicy } from '@ablo/
|
|
8
|
+
* import { type ConflictPolicy, defaultPolicy } from '@abloatai/ablo/policy';
|
|
9
9
|
*
|
|
10
10
|
* export const myPolicy: ConflictPolicy = (ctx) => {
|
|
11
11
|
* if (ctx.committer.id.startsWith('linter:')) {
|
package/dist/policy/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/policy — pluggable conflict resolution.
|
|
3
3
|
*
|
|
4
4
|
* The engine detects conflicts; the policy decides. Customer code
|
|
5
5
|
* implements `ConflictPolicy` and registers it at the sync-server.
|
|
6
6
|
*
|
|
7
7
|
* ```ts
|
|
8
|
-
* import { type ConflictPolicy, defaultPolicy } from '@ablo/
|
|
8
|
+
* import { type ConflictPolicy, defaultPolicy } from '@abloatai/ablo/policy';
|
|
9
9
|
*
|
|
10
10
|
* export const myPolicy: ConflictPolicy = (ctx) => {
|
|
11
11
|
* if (ctx.committer.id.startsWith('linter:')) {
|
package/dist/principal.d.ts
CHANGED
package/dist/principal.js
CHANGED
|
@@ -15,7 +15,7 @@ import { type ReactNode } from 'react';
|
|
|
15
15
|
*
|
|
16
16
|
* v0.3.x implementation is non-Suspense: reads `useSyncStatus()` and
|
|
17
17
|
* conditionally renders. v0.3.x+ will ship a
|
|
18
|
-
* `@ablo/
|
|
18
|
+
* `@abloatai/ablo/react/suspense` subpath where `useQuery` / `useOne`
|
|
19
19
|
* actually throw Promises; this component becomes a thin wrapper around
|
|
20
20
|
* React's real `<Suspense>` at that point.
|
|
21
21
|
*
|
|
@@ -38,7 +38,7 @@ export function useSyncGroup() {
|
|
|
38
38
|
if (!id) {
|
|
39
39
|
throw new AbloValidationError('useSyncGroup: no <SyncGroupProvider> mounted above this component. ' +
|
|
40
40
|
'Wrap your tree with <SyncGroupProvider id="matter:..."> from ' +
|
|
41
|
-
'@ablo/
|
|
41
|
+
'@abloatai/ablo/react.', { code: 'no_sync_group_provider' });
|
|
42
42
|
}
|
|
43
43
|
return id;
|
|
44
44
|
}
|
package/dist/react/context.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ export interface SyncProviderProps {
|
|
|
148
148
|
* (useModel, useModels, useMutations) can access it.
|
|
149
149
|
*
|
|
150
150
|
* @example
|
|
151
|
-
* import { SyncProvider } from '@ablo/
|
|
151
|
+
* import { SyncProvider } from '@abloatai/ablo/react';
|
|
152
152
|
*
|
|
153
153
|
* function App() {
|
|
154
154
|
* return (
|
package/dist/react/context.js
CHANGED
|
@@ -20,7 +20,7 @@ export function useSyncContext() {
|
|
|
20
20
|
* (useModel, useModels, useMutations) can access it.
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
|
-
* import { SyncProvider } from '@ablo/
|
|
23
|
+
* import { SyncProvider } from '@abloatai/ablo/react';
|
|
24
24
|
*
|
|
25
25
|
* function App() {
|
|
26
26
|
* return (
|
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED