@abloatai/ablo 0.3.1 → 0.5.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 +54 -1
- package/NOTICE +2 -2
- package/README.md +99 -78
- package/dist/BaseSyncedStore.d.ts +3 -2
- 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 +53 -27
- 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 +118 -32
- package/dist/client/createModelProxy.js +87 -44
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +3 -3
- 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 +9 -7
- package/dist/errors.js +9 -7
- package/dist/index.d.ts +20 -6
- package/dist/index.js +41 -22
- 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/policy/types.d.ts +10 -0
- package/dist/principal.d.ts +3 -3
- package/dist/principal.js +3 -3
- package/dist/query/client.d.ts +7 -6
- package/dist/react/AbloProvider.d.ts +44 -1
- package/dist/react/AbloProvider.js +3 -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 +20 -5
- 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 +41 -1
- 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
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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed error hierarchy for `@ablo
|
|
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
|
|
@@ -186,16 +186,18 @@ export interface CommitReceipt {
|
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
189
|
+
* A scoped credential was denied — either the key is unknown / revoked /
|
|
190
|
+
* expired (`capability_invalid`), or the connection's resolved scope
|
|
191
|
+
* doesn't cover the attempted action (`capability_scope_denied`). With
|
|
192
|
+
* opaque restricted (`rk_`) API keys this is a server-side check against
|
|
193
|
+
* the key's `syncGroups` / `operations`, not a signed-caveat verification.
|
|
192
194
|
*
|
|
193
195
|
* Extends `AbloPermissionError` so existing `instanceof CapabilityError`
|
|
194
196
|
* checks keep working AND broader `instanceof AbloPermissionError`
|
|
195
|
-
* matches for consumers who don't care about the
|
|
197
|
+
* matches for consumers who don't care about the scope specifics.
|
|
196
198
|
*
|
|
197
|
-
* `requiredCapability` (when present) describes
|
|
198
|
-
*
|
|
199
|
+
* `requiredCapability` (when present) describes the scope a key must
|
|
200
|
+
* carry for the request to succeed on retry.
|
|
199
201
|
*/
|
|
200
202
|
export declare class CapabilityError extends AbloPermissionError {
|
|
201
203
|
readonly requiredCapability?: RequiredCapability;
|
package/dist/errors.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed error hierarchy for `@ablo
|
|
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
|
|
@@ -125,16 +125,18 @@ export class AbloBusyError extends AbloError {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
128
|
+
* A scoped credential was denied — either the key is unknown / revoked /
|
|
129
|
+
* expired (`capability_invalid`), or the connection's resolved scope
|
|
130
|
+
* doesn't cover the attempted action (`capability_scope_denied`). With
|
|
131
|
+
* opaque restricted (`rk_`) API keys this is a server-side check against
|
|
132
|
+
* the key's `syncGroups` / `operations`, not a signed-caveat verification.
|
|
131
133
|
*
|
|
132
134
|
* Extends `AbloPermissionError` so existing `instanceof CapabilityError`
|
|
133
135
|
* checks keep working AND broader `instanceof AbloPermissionError`
|
|
134
|
-
* matches for consumers who don't care about the
|
|
136
|
+
* matches for consumers who don't care about the scope specifics.
|
|
135
137
|
*
|
|
136
|
-
* `requiredCapability` (when present) describes
|
|
137
|
-
*
|
|
138
|
+
* `requiredCapability` (when present) describes the scope a key must
|
|
139
|
+
* carry for the request to succeed on retry.
|
|
138
140
|
*/
|
|
139
141
|
export class CapabilityError extends AbloPermissionError {
|
|
140
142
|
requiredCapability;
|
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,30 @@
|
|
|
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
|
+
*
|
|
30
|
+
* ── What to import (read this first) ────────────────────────────────
|
|
31
|
+
* Default path — this is all most apps and agents ever need:
|
|
32
|
+
* • `Ablo` (default export) + `AbloOptions` + the `Model*Options` bags
|
|
33
|
+
* • the `Ablo*Error` classes, to discriminate failures in catch blocks
|
|
34
|
+
* That's it. If you're reaching past those, you're in advanced territory.
|
|
35
|
+
*
|
|
36
|
+
* Advanced — opt-in, most apps never import these (each is tagged
|
|
37
|
+
* "Advanced —" at its export below, with the one situation it's for):
|
|
38
|
+
* • `dataSource` / `abloSource` — only if your own DB stays canonical
|
|
39
|
+
* • `session` / `agent` — only for delegated agent principals
|
|
40
|
+
* • `defaultPolicy` — only to customize conflict resolution
|
|
41
|
+
* • `defineMutators` / `createTransaction` — only for custom mutators
|
|
42
|
+
* If you don't recognize one, you don't need it — the default path covers you.
|
|
29
43
|
*/
|
|
30
44
|
export { Ablo } from './client/Ablo.js';
|
|
31
|
-
export type { AbloOptions, ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions,
|
|
45
|
+
export type { AbloOptions, ModelCountOptions, ModelListOptions, ModelListScope, ModelLoadOptions, ModelIntentHandle, ModelIntentAcquireOptions, ModelOperations, } from './client/Ablo.js';
|
|
32
46
|
export type { AbloPersistence } from './client/persistence.js';
|
|
33
47
|
export { session, agent } from './principal.js';
|
|
34
48
|
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,44 +19,61 @@
|
|
|
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
|
+
*
|
|
30
|
+
* ── What to import (read this first) ────────────────────────────────
|
|
31
|
+
* Default path — this is all most apps and agents ever need:
|
|
32
|
+
* • `Ablo` (default export) + `AbloOptions` + the `Model*Options` bags
|
|
33
|
+
* • the `Ablo*Error` classes, to discriminate failures in catch blocks
|
|
34
|
+
* That's it. If you're reaching past those, you're in advanced territory.
|
|
35
|
+
*
|
|
36
|
+
* Advanced — opt-in, most apps never import these (each is tagged
|
|
37
|
+
* "Advanced —" at its export below, with the one situation it's for):
|
|
38
|
+
* • `dataSource` / `abloSource` — only if your own DB stays canonical
|
|
39
|
+
* • `session` / `agent` — only for delegated agent principals
|
|
40
|
+
* • `defaultPolicy` — only to customize conflict resolution
|
|
41
|
+
* • `defineMutators` / `createTransaction` — only for custom mutators
|
|
42
|
+
* If you don't recognize one, you don't need it — the default path covers you.
|
|
29
43
|
*/
|
|
30
44
|
// ── Consumer API ──────────────────────────────────────────────────────────
|
|
31
45
|
// These are the only symbols external consumers should need from this path.
|
|
32
46
|
// Everything else is in a subpath.
|
|
33
47
|
// The canonical surface — `Ablo` is a function, type, and namespace under
|
|
34
48
|
// one name. Matches `Stripe`, `OpenAI`, `Anthropic`. Default export so
|
|
35
|
-
// `import Ablo from '@ablo
|
|
49
|
+
// `import Ablo from '@abloatai/ablo'` works; named export so
|
|
36
50
|
// `import { Ablo }` also compiles.
|
|
37
51
|
export { Ablo } from './client/Ablo.js';
|
|
38
52
|
// Participant types live under `Ablo.Participant.*` —
|
|
39
53
|
// `Ablo.Participant.Joined`, `Ablo.Participant.Manager`,
|
|
40
54
|
// `Ablo.Participant.JoinOptions`, etc. Same dot-access shape as
|
|
41
55
|
// `Ablo.Peer`, `Ablo.Claim`, `Ablo.Turn`. No flat re-exports.
|
|
42
|
-
//
|
|
43
|
-
// (`Ablo({ kind: 'agent', as: session({...}) })`).
|
|
44
|
-
//
|
|
56
|
+
// Advanced — most apps never import this. Principal constructors for
|
|
57
|
+
// delegated agent paths (`Ablo({ kind: 'agent', as: session({...}) })`).
|
|
58
|
+
// The default `Ablo({ schema, apiKey })` resolves identity from the key;
|
|
59
|
+
// reach for these only when minting a delegated agent principal.
|
|
45
60
|
export { session, agent } from './principal.js';
|
|
46
61
|
import { Ablo } from './client/Ablo.js';
|
|
47
62
|
export default Ablo;
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
63
|
+
// Advanced — most apps never import this. Customer-owned storage adapter
|
|
64
|
+
// for Data Source mode: only when Ablo Cloud coordinates state while
|
|
65
|
+
// canonical rows stay in YOUR database. The default is Ablo-managed
|
|
66
|
+
// storage — if you haven't deliberately chosen to keep your own DB
|
|
67
|
+
// canonical, skip this entirely. Type counterparts live under
|
|
68
|
+
// `Ablo.Source.*` (`Ablo.Source.Operation`, `Ablo.Source.Commit.Params`).
|
|
52
69
|
export { dataSource, abloSource, signAbloSourceRequest, verifyAbloSourceRequest, } from './source/index.js';
|
|
53
|
-
// Schema DSL is intentionally published from `@ablo/
|
|
70
|
+
// Schema DSL is intentionally published from `@abloatai/ablo/schema`.
|
|
54
71
|
// Keeping it out of the root import preserves one clean runtime surface:
|
|
55
|
-
// `import Ablo from '@ablo
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
72
|
+
// `import Ablo from '@abloatai/ablo'`.
|
|
73
|
+
// Advanced — most apps never import this. Conflict policy: `defaultPolicy`
|
|
74
|
+
// (reject-on-stale) is already applied server-side, so you only import it
|
|
75
|
+
// to COMPOSE a custom policy. Leave it alone and stale writes are rejected
|
|
76
|
+
// safely by default. Type counterparts live under `Ablo.Conflict.*`.
|
|
60
77
|
export { defaultPolicy } from './policy/index.js';
|
|
61
78
|
// Typed error hierarchy — Stripe-style. One import gets every class
|
|
62
79
|
// consumers need to discriminate failures (`e instanceof AbloX` or
|
|
@@ -66,8 +83,10 @@ export { SyncSessionError, AbloError, AbloAuthenticationError, AbloPermissionErr
|
|
|
66
83
|
// Intents/UserMeta once in a `.d.ts` via `declare global { interface AbloSync
|
|
67
84
|
// { ... } }`. Resolver types live under the `Ablo` namespace —
|
|
68
85
|
// `Ablo.ResolveSchema`, `Ablo.ResolvePresence`, etc. — pure type-level.
|
|
69
|
-
//
|
|
70
|
-
//
|
|
86
|
+
// Advanced — most apps never import this. Custom (Zero-style) mutators:
|
|
87
|
+
// `ablo.<model>.create/update/delete` already covers normal writes. Reach
|
|
88
|
+
// for `defineMutators` only when you need a named, multi-step mutation with
|
|
89
|
+
// custom undo. Type counterparts live under the `Ablo` namespace:
|
|
71
90
|
// Ablo.Mutator.Fn, Ablo.Transaction
|
|
72
91
|
// Ablo.Mutator.UndoEntry, Ablo.Mutator.InverseOp
|
|
73
92
|
// Ablo.Query, Ablo.QueryBatch, Ablo.QueryBatchResult
|
|
@@ -79,4 +98,4 @@ export { defineMutators } from './mutators/defineMutators.js';
|
|
|
79
98
|
// pass it as `{ tx, args }` to the mutator function.
|
|
80
99
|
export { createTransaction } from './mutators/Transaction.js';
|
|
81
100
|
// Undo runtime is intentionally not part of the public root surface. App code
|
|
82
|
-
// uses `useUndoScope` from `@ablo/
|
|
101
|
+
// 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/policy/types.d.ts
CHANGED
|
@@ -29,6 +29,16 @@ export interface StaleContextConflict extends ConflictBase {
|
|
|
29
29
|
readonly readAt: number;
|
|
30
30
|
/** Most recent delta id on the target. */
|
|
31
31
|
readonly observedSyncId: number;
|
|
32
|
+
/**
|
|
33
|
+
* The fields whose concurrent change triggered this conflict — the
|
|
34
|
+
* intersection of the committer's written fields and the columns a
|
|
35
|
+
* newer delta touched. Empty array means the conflicting delta was a
|
|
36
|
+
* whole-entity change (CREATE/DELETE, or a pre-`changed_fields`
|
|
37
|
+
* legacy delta), which conflicts with any write. Lets a policy decide
|
|
38
|
+
* at field granularity, e.g. allow when the only collision is on a
|
|
39
|
+
* cosmetic field. See `docs/internal/per-field-conflict-detection.md`.
|
|
40
|
+
*/
|
|
41
|
+
readonly conflictingFields?: readonly string[];
|
|
32
42
|
}
|
|
33
43
|
export interface IntentHeldConflict extends ConflictBase {
|
|
34
44
|
readonly kind: 'intent_held';
|
package/dist/principal.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* the discriminated-union tags.
|
|
5
5
|
*
|
|
6
6
|
* ```ts
|
|
7
|
-
* import Ablo, { session } from '@ablo
|
|
7
|
+
* import Ablo, { session } from '@abloatai/ablo';
|
|
8
8
|
*
|
|
9
9
|
* const ablo = Ablo({ schema, apiKey });
|
|
10
10
|
* const participant = await ablo.participants.join({
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* participant layer handles attenuation.
|
|
19
19
|
*
|
|
20
20
|
* These are pure — no I/O, no hidden state. If the shape ever grows a
|
|
21
|
-
* required field (say, a
|
|
22
|
-
* place to flag migrations.
|
|
21
|
+
* required field (say, a scope hint for the restricted key), the helper
|
|
22
|
+
* is the one place to flag migrations.
|
|
23
23
|
*/
|
|
24
24
|
import type { AgentRef, SessionRef } from './types/streams.js';
|
|
25
25
|
/**
|
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
|
|
7
|
+
* import Ablo, { session } from '@abloatai/ablo';
|
|
8
8
|
*
|
|
9
9
|
* const ablo = Ablo({ schema, apiKey });
|
|
10
10
|
* const participant = await ablo.participants.join({
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* participant layer handles attenuation.
|
|
19
19
|
*
|
|
20
20
|
* These are pure — no I/O, no hidden state. If the shape ever grows a
|
|
21
|
-
* required field (say, a
|
|
22
|
-
* place to flag migrations.
|
|
21
|
+
* required field (say, a scope hint for the restricted key), the helper
|
|
22
|
+
* is the one place to flag migrations.
|
|
23
23
|
*/
|
|
24
24
|
/**
|
|
25
25
|
* Build a `SessionRef` from the identifiers your auth system already
|
package/dist/query/client.d.ts
CHANGED
|
@@ -22,13 +22,14 @@ export interface PostQueryOptions {
|
|
|
22
22
|
/** Timeout in ms for the fetch request. Default: 30000. */
|
|
23
23
|
fetchTimeout?: number;
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
* Required for Node consumers
|
|
27
|
-
* have no session cookie to
|
|
28
|
-
*
|
|
29
|
-
*
|
|
25
|
+
* Bearer credential — a restricted (`rk_`) API key — attached as
|
|
26
|
+
* `Authorization: Bearer <token>`. Required for Node consumers
|
|
27
|
+
* (agent-worker, server-side tests) that have no session cookie to
|
|
28
|
+
* ride. Browser consumers can omit this and fall back to
|
|
29
|
+
* `credentials: 'include'`. When both are present the server prefers
|
|
30
|
+
* the Bearer header (see `apiKeyProvider` in
|
|
30
31
|
* `apps/sync-server/src/auth`), so passing the token in browser code
|
|
31
|
-
* is harmless.
|
|
32
|
+
* is harmless. (Field name predates the Biscuit→opaque-key migration.)
|
|
32
33
|
*/
|
|
33
34
|
capabilityToken?: string;
|
|
34
35
|
}
|
|
@@ -29,8 +29,29 @@ import { type SyncStoreContract } from './context.js';
|
|
|
29
29
|
* only when `userId` / account scope / `url` change. React
|
|
30
30
|
* Strict Mode double-mount does not leak a second WebSocket.
|
|
31
31
|
*/
|
|
32
|
+
/**
|
|
33
|
+
* Props for `<AbloProvider>`.
|
|
34
|
+
*
|
|
35
|
+
* The default path is one prop:
|
|
36
|
+
*
|
|
37
|
+
* ```tsx
|
|
38
|
+
* <AbloProvider schema={schema}>
|
|
39
|
+
* <App />
|
|
40
|
+
* </AbloProvider>
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* That's it for most apps — the provider resolves identity, account
|
|
44
|
+
* scope, and realtime permissions from auth. `userId`/`apiKey`/`url`
|
|
45
|
+
* are situational; the `bootstrapMode`, `persistence`, and `fallback`
|
|
46
|
+
* props are opt-in tuning; and the block tagged "Optional DI (advanced)"
|
|
47
|
+
* below is escape-hatch wiring for tests and platform builders — if you
|
|
48
|
+
* don't recognize a prop there, you don't need it.
|
|
49
|
+
*/
|
|
32
50
|
export interface AbloProviderProps<R extends SchemaRecord = SchemaRecord> {
|
|
33
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Schema from `defineSchema()`. Determines the typed hook surface.
|
|
53
|
+
* This is the only prop most apps pass — start here.
|
|
54
|
+
*/
|
|
34
55
|
schema: Schema<R>;
|
|
35
56
|
/**
|
|
36
57
|
* WebSocket URL of the sync server (`wss://...` or `ws://...`).
|
|
@@ -103,6 +124,28 @@ export interface AbloProviderProps<R extends SchemaRecord = SchemaRecord> {
|
|
|
103
124
|
* for the full semantics.
|
|
104
125
|
*/
|
|
105
126
|
persistence?: AbloPersistence;
|
|
127
|
+
/**
|
|
128
|
+
* How aggressively this provider pulls baseline state at startup.
|
|
129
|
+
*
|
|
130
|
+
* - `'full'` (default): pull every delta in the configured sync
|
|
131
|
+
* groups before the engine reports ready — a local replica of the
|
|
132
|
+
* org's tenant plane. Right for collaborative editors and any page
|
|
133
|
+
* that reads a lot of shared state.
|
|
134
|
+
* - `'none'`: open the connection and process live deltas only — no
|
|
135
|
+
* baseline fetch. Reads round-trip via `ablo.<model>.retrieve(...)`
|
|
136
|
+
* and subscriptions populate the pool lazily. Right for read-light
|
|
137
|
+
* pages (a mostly-static dashboard, a settings screen) that don't
|
|
138
|
+
* want to download the whole org to render.
|
|
139
|
+
*
|
|
140
|
+
* Note: `'none'` still opens the realtime connection — it skips the
|
|
141
|
+
* baseline pull, not the socket. A fully connection-free mode for
|
|
142
|
+
* pages that do zero multiplayer is a separate follow-up (the socket
|
|
143
|
+
* open lives inside `engine.ready()`, so deferring it needs
|
|
144
|
+
* engine-level lazy-connect support, not just a provider prop).
|
|
145
|
+
*
|
|
146
|
+
* Mirrors `AbloOptions.bootstrapMode`. Changing it rotates the engine.
|
|
147
|
+
*/
|
|
148
|
+
bootstrapMode?: 'full' | 'none';
|
|
106
149
|
/**
|
|
107
150
|
* Rendered in place of `children` during the *first* bootstrap pass —
|
|
108
151
|
* while the engine is actively transitioning from `initial` →
|
|
@@ -32,7 +32,7 @@ function createErrorEmitter() {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
export function AbloProvider(props) {
|
|
35
|
-
const { schema, url = 'wss://mesh.ablo.finance', userId, teamIds, apiKey, preventUnsavedChanges, onSessionExpired, onError, observability, logger, mutationExecutor, mutationDispatcher, sessionErrorDetector, onlineStatus, configOverrides, syncGroups, bootstrapBaseUrl, maxPoolSize, persistence, fallback = _jsx(DefaultFallback, {}), children, } = props;
|
|
35
|
+
const { schema, url = 'wss://mesh.ablo.finance', userId, teamIds, apiKey, preventUnsavedChanges, onSessionExpired, onError, observability, logger, mutationExecutor, mutationDispatcher, sessionErrorDetector, onlineStatus, configOverrides, syncGroups, bootstrapBaseUrl, maxPoolSize, persistence, bootstrapMode, fallback = _jsx(DefaultFallback, {}), children, } = props;
|
|
36
36
|
// Account scope is no longer accepted from props. The engine learns
|
|
37
37
|
// it from auth (capability token) at bootstrap and we read it back
|
|
38
38
|
// out of `_store.orgId` once `engine.ready()` resolves.
|
|
@@ -62,6 +62,7 @@ export function AbloProvider(props) {
|
|
|
62
62
|
const engineKey = JSON.stringify({
|
|
63
63
|
userId: userId ?? null,
|
|
64
64
|
url,
|
|
65
|
+
bootstrapMode: bootstrapMode ?? null,
|
|
65
66
|
});
|
|
66
67
|
const [engineState, setEngineState] = useState({ key: engineKey, engine: null });
|
|
67
68
|
// Keep a ref to the current engine key so the rotation effect can
|
|
@@ -89,6 +90,7 @@ export function AbloProvider(props) {
|
|
|
89
90
|
bootstrapBaseUrl,
|
|
90
91
|
maxPoolSize,
|
|
91
92
|
persistence,
|
|
93
|
+
...(bootstrapMode ? { bootstrapMode } : {}),
|
|
92
94
|
autoStart: false,
|
|
93
95
|
};
|
|
94
96
|
const engine = Ablo(engineOptions);
|
|
@@ -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
|
@@ -27,7 +27,7 @@ export function useCurrentUserId() {
|
|
|
27
27
|
if (!ctx) {
|
|
28
28
|
throw new AbloValidationError('useCurrentUserId: no <AbloProvider> mounted above this component. ' +
|
|
29
29
|
'Wrap your tree with <AbloProvider ...> from ' +
|
|
30
|
-
'@ablo/
|
|
30
|
+
'@abloatai/ablo/react.', { code: 'no_ablo_provider' });
|
|
31
31
|
}
|
|
32
32
|
return ctx.currentUserId;
|
|
33
33
|
}
|
|
@@ -25,7 +25,7 @@ export function useErrorListener(listener) {
|
|
|
25
25
|
const ctx = useContext(AbloInternalContext);
|
|
26
26
|
if (!ctx) {
|
|
27
27
|
throw new AbloValidationError('useErrorListener: no <AbloProvider> mounted above this component. ' +
|
|
28
|
-
'Wrap your tree with <AbloProvider ...> from @ablo/
|
|
28
|
+
'Wrap your tree with <AbloProvider ...> from @abloatai/ablo/react.', { code: 'no_ablo_provider' });
|
|
29
29
|
}
|
|
30
30
|
// Stash the latest callback in a ref so the effect subscription
|
|
31
31
|
// stays stable across renders. Matches the `useEventCallback`
|
|
@@ -13,7 +13,7 @@ type GlobalMutateActions<K extends string> = ResolveSchema extends Schema ? K ex
|
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* import { schema } from '@ablo/schema';
|
|
16
|
-
* import { useMutate } from '@ablo/
|
|
16
|
+
* import { useMutate } from '@abloatai/ablo/react';
|
|
17
17
|
*
|
|
18
18
|
* const tasks = useMutate(schema, 'tasks');
|
|
19
19
|
*
|
|
@@ -25,7 +25,7 @@ export function useMutationFailureListener(listener) {
|
|
|
25
25
|
const ctx = useContext(AbloInternalContext);
|
|
26
26
|
if (!ctx) {
|
|
27
27
|
throw new AbloValidationError('useMutationFailureListener: no <AbloProvider> mounted above this component. ' +
|
|
28
|
-
'Wrap your tree with <AbloProvider ...> from @ablo/
|
|
28
|
+
'Wrap your tree with <AbloProvider ...> from @abloatai/ablo/react.', { code: 'no_ablo_provider' });
|
|
29
29
|
}
|
|
30
30
|
const ref = useRef(listener);
|
|
31
31
|
ref.current = listener;
|
|
@@ -21,7 +21,7 @@ type GlobalReaderActions<K extends string> = ResolveSchema extends Schema ? K ex
|
|
|
21
21
|
*
|
|
22
22
|
* @example
|
|
23
23
|
* import { schema } from '@ablo/schema';
|
|
24
|
-
* import { useReader } from '@ablo/
|
|
24
|
+
* import { useReader } from '@abloatai/ablo/react';
|
|
25
25
|
*
|
|
26
26
|
* function useTaskMutations() {
|
|
27
27
|
* const read = useReader(schema, 'tasks');
|
package/dist/schema/field.d.ts
CHANGED
package/dist/schema/field.js
CHANGED
package/dist/schema/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @ablo/
|
|
2
|
+
* @abloatai/ablo/schema — Schema Definition DSL
|
|
3
3
|
*
|
|
4
4
|
* Define your data models with Zod. Types are inferred automatically.
|
|
5
5
|
*
|
|
6
6
|
* ```ts
|
|
7
7
|
* import { z } from 'zod';
|
|
8
|
-
* import { defineSchema, model, relation } from '@ablo/
|
|
8
|
+
* import { defineSchema, model, relation } from '@abloatai/ablo/schema';
|
|
9
9
|
*
|
|
10
10
|
* export const schema = defineSchema({
|
|
11
11
|
* tasks: model({
|