@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.
Files changed (87) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/NOTICE +2 -2
  3. package/README.md +27 -25
  4. package/dist/agent/Agent.d.ts +1 -1
  5. package/dist/agent/Agent.js +1 -1
  6. package/dist/agent/index.d.ts +4 -4
  7. package/dist/agent/index.js +6 -6
  8. package/dist/agent/types.d.ts +1 -1
  9. package/dist/ai-sdk/index.d.ts +3 -3
  10. package/dist/ai-sdk/index.js +3 -3
  11. package/dist/ai-sdk/intent-broadcast.d.ts +1 -1
  12. package/dist/ai-sdk/intent-broadcast.js +1 -1
  13. package/dist/auth/index.d.ts +1 -1
  14. package/dist/client/Ablo.d.ts +8 -14
  15. package/dist/client/Ablo.js +32 -1
  16. package/dist/client/auth.d.ts +3 -3
  17. package/dist/client/auth.js +5 -5
  18. package/dist/client/createModelProxy.d.ts +110 -32
  19. package/dist/client/createModelProxy.js +77 -38
  20. package/dist/client/index.d.ts +2 -2
  21. package/dist/client/index.js +2 -2
  22. package/dist/config/index.d.ts +1 -1
  23. package/dist/config/index.js +1 -1
  24. package/dist/core/index.d.ts +1 -1
  25. package/dist/core/index.js +2 -2
  26. package/dist/errors.d.ts +1 -1
  27. package/dist/errors.js +1 -1
  28. package/dist/index.d.ts +6 -6
  29. package/dist/index.js +9 -9
  30. package/dist/interfaces/headless.d.ts +1 -1
  31. package/dist/interfaces/headless.js +2 -2
  32. package/dist/policy/index.d.ts +2 -2
  33. package/dist/policy/index.js +2 -2
  34. package/dist/principal.d.ts +1 -1
  35. package/dist/principal.js +1 -1
  36. package/dist/react/ClientSideSuspense.d.ts +1 -1
  37. package/dist/react/SyncGroupProvider.js +1 -1
  38. package/dist/react/context.d.ts +1 -1
  39. package/dist/react/context.js +1 -1
  40. package/dist/react/index.d.ts +1 -1
  41. package/dist/react/index.js +1 -1
  42. package/dist/react/useCurrentUserId.js +1 -1
  43. package/dist/react/useErrorListener.js +1 -1
  44. package/dist/react/useMutate.d.ts +1 -1
  45. package/dist/react/useMutationFailureListener.js +1 -1
  46. package/dist/react/useReader.d.ts +1 -1
  47. package/dist/schema/field.d.ts +1 -1
  48. package/dist/schema/field.js +1 -1
  49. package/dist/schema/index.d.ts +2 -2
  50. package/dist/schema/index.js +2 -2
  51. package/dist/schema/model.d.ts +2 -2
  52. package/dist/schema/model.js +2 -2
  53. package/dist/schema/queries.d.ts +1 -1
  54. package/dist/schema/queries.js +1 -1
  55. package/dist/schema/relation.d.ts +1 -1
  56. package/dist/schema/relation.js +1 -1
  57. package/dist/schema/schema.d.ts +1 -1
  58. package/dist/schema/schema.js +1 -1
  59. package/dist/source/index.d.ts +22 -28
  60. package/dist/source/index.js +23 -20
  61. package/dist/source/pushQueue.d.ts +1 -1
  62. package/dist/source/pushQueue.js +2 -2
  63. package/dist/sync/SyncWebSocket.d.ts +14 -0
  64. package/dist/sync/createIntentStream.js +7 -0
  65. package/dist/testing/fixtures/models.d.ts +1 -1
  66. package/dist/testing/fixtures/models.js +1 -1
  67. package/dist/testing/helpers/react-wrapper.d.ts +2 -2
  68. package/dist/testing/helpers/react-wrapper.js +2 -2
  69. package/dist/testing/index.d.ts +1 -1
  70. package/dist/testing/index.js +1 -1
  71. package/dist/types/streams.d.ts +39 -0
  72. package/docs/api.md +78 -20
  73. package/docs/data-sources.md +50 -16
  74. package/docs/examples/ai-sdk-tool.md +14 -31
  75. package/docs/examples/existing-python-backend.md +6 -6
  76. package/docs/integration-guide.md +8 -7
  77. package/docs/interaction-model.md +16 -4
  78. package/docs/mcp.md +1 -1
  79. package/docs/quickstart.md +20 -18
  80. package/examples/data-source/README.md +1 -1
  81. package/examples/data-source/ablo-driver.ts +5 -5
  82. package/examples/data-source/customer-server.ts +10 -10
  83. package/examples/data-source/run.ts +9 -11
  84. package/examples/data-source/schema.ts +1 -1
  85. package/examples/quickstart.ts +2 -2
  86. package/llms.txt +1 -1
  87. 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
- * `edit`,
12
- * `subscribe`, and `load`. The factory returns a plain object; the
13
- * client assembles `ablo.<model>` lookup table from these.
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 ModelEditOptions<T = Record<string, unknown>> {
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<ModelIntentHandle>;
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
- * Start a model-scoped activity lease for long-running AI or background work.
139
- * Other participants can see the activity until `update`, `release`, or TTL.
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
- edit(id: string, options?: ModelEditOptions<T>): Promise<ModelEditHandle<T>>;
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
- * `edit`,
12
- * `subscribe`, and `load`. The factory returns a plain object; the
13
- * client assembles `ablo.<model>` lookup table from these.
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
- async edit(id, options) {
115
- if (!collaboration) {
116
- throw new AbloValidationError(`Model "${schemaKey}" cannot start edit activity without collaboration wiring.`, { code: 'model_edit_not_configured' });
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.signal.removeEventListener('abort', revoke);
143
- intent.revoke();
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.signal.removeEventListener('abort', revoke);
150
- await intent.release();
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
- intentId: intent.id,
156
- activity,
157
- current: modelAsRow(model),
158
- signal: snapshot.signal,
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(`Edit context is stale for ${schemaKey}/${id}. Re-read the row and retry.`, {
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: options?.wait ?? 'confirmed',
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
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/client — Consumer API
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/sync-engine/client';
10
+ * import { Ablo } from '@abloatai/ablo/client';
11
11
  * import { schema } from './schema';
12
12
  *
13
13
  * const ablo = Ablo({
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/client — Consumer API
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/sync-engine/client';
10
+ * import { Ablo } from '@abloatai/ablo/client';
11
11
  * import { schema } from './schema';
12
12
  *
13
13
  * const ablo = Ablo({
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/config — App initialization
2
+ * @abloatai/ablo/config — App initialization
3
3
  *
4
4
  * One-time setup at app boot. Provides DI interface types
5
5
  * and the initSyncEngine() function to wire real implementations.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/config — App initialization
2
+ * @abloatai/ablo/config — App initialization
3
3
  *
4
4
  * One-time setup at app boot. Provides DI interface types
5
5
  * and the initSyncEngine() function to wire real implementations.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine-internal/core — Framework extension
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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine-internal/core — Framework extension
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/sync-engine/react` is
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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Typed error hierarchy for `@ablo/sync-engine`.
2
+ * Typed error hierarchy for `@abloatai/ablo`.
3
3
  *
4
4
  * Inlined directly so the publishable dist is self-contained. The public
5
5
  * package should never reference an unpublished internal package from emitted
package/dist/errors.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Typed error hierarchy for `@ablo/sync-engine`.
2
+ * Typed error hierarchy for `@abloatai/ablo`.
3
3
  *
4
4
  * Inlined directly so the publishable dist is self-contained. The public
5
5
  * package should never reference an unpublished internal package from emitted
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @ablo/sync-engine — The Collaboration Layer for AI and Humans
2
+ * @abloatai/ablo — The Collaboration Layer for AI and Humans
3
3
  *
4
4
  * ```ts
5
- * import Ablo from '@ablo/sync-engine';
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/sync-engine/schema — defineSchema, model, z (Zod)
23
- * @ablo/sync-engine/react — <AbloProvider>, useQuery, useMutate
24
- * @ablo/sync-engine/testing — test harnesses + mocks
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, ModelEditHandle, ModelEditOptions, ModelOperations, } from './client/Ablo.js';
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/sync-engine — The Collaboration Layer for AI and Humans
2
+ * @abloatai/ablo — The Collaboration Layer for AI and Humans
3
3
  *
4
4
  * ```ts
5
- * import Ablo from '@ablo/sync-engine';
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/sync-engine/schema — defineSchema, model, z (Zod)
23
- * @ablo/sync-engine/react — <AbloProvider>, useQuery, useMutate
24
- * @ablo/sync-engine/testing — test harnesses + mocks
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/sync-engine'` works; named export so
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/sync-engine/schema`.
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/sync-engine'`.
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/sync-engine/react`.
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/sync-engine/headless';
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/sync-engine/headless';
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/sync-engine/headless';
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 { ... }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @ablo/sync-engine/policy — pluggable conflict resolution.
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/sync-engine/policy';
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:')) {
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @ablo/sync-engine/policy — pluggable conflict resolution.
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/sync-engine/policy';
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:')) {
@@ -4,7 +4,7 @@
4
4
  * the discriminated-union tags.
5
5
  *
6
6
  * ```ts
7
- * import Ablo, { session } from '@ablo/sync-engine';
7
+ * import Ablo, { session } from '@abloatai/ablo';
8
8
  *
9
9
  * const ablo = Ablo({ schema, apiKey });
10
10
  * const participant = await ablo.participants.join({
package/dist/principal.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * the discriminated-union tags.
5
5
  *
6
6
  * ```ts
7
- * import Ablo, { session } from '@ablo/sync-engine';
7
+ * import Ablo, { session } from '@abloatai/ablo';
8
8
  *
9
9
  * const ablo = Ablo({ schema, apiKey });
10
10
  * const participant = await ablo.participants.join({
@@ -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/sync-engine/react/suspense` subpath where `useQuery` / `useOne`
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/sync-engine/react.', { code: 'no_sync_group_provider' });
41
+ '@abloatai/ablo/react.', { code: 'no_sync_group_provider' });
42
42
  }
43
43
  return id;
44
44
  }
@@ -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/sync-engine/react';
151
+ * import { SyncProvider } from '@abloatai/ablo/react';
152
152
  *
153
153
  * function App() {
154
154
  * return (
@@ -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/sync-engine/react';
23
+ * import { SyncProvider } from '@abloatai/ablo/react';
24
24
  *
25
25
  * function App() {
26
26
  * return (
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/react — React bindings (v0.3.0)
2
+ * @abloatai/ablo/react — React bindings (v0.3.0)
3
3
  *
4
4
  * Umbrella provider:
5
5
  * <AbloProvider schema={...} userId={...} orgId={...} fallback={<Skeleton/>}>
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @ablo/sync-engine/react — React bindings (v0.3.0)
2
+ * @abloatai/ablo/react — React bindings (v0.3.0)
3
3
  *
4
4
  * Umbrella provider:
5
5
  * <AbloProvider schema={...} userId={...} orgId={...} fallback={<Skeleton/>}>