@abloatai/ablo 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -4
- package/CHANGELOG.md +199 -13
- package/README.md +2 -1
- package/dist/ai-sdk.d.ts +1 -1
- package/dist/ai-sdk.d.ts.map +1 -1
- package/dist/context/evidence.d.ts +6 -8
- package/dist/context/evidence.d.ts.map +1 -1
- package/dist/context/evidence.js +6 -20
- package/dist/context/evidence.js.map +1 -1
- package/dist/context/index.d.ts +23 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +26 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/onChange.d.ts +9 -0
- package/dist/context/onChange.d.ts.map +1 -0
- package/dist/context/onChange.js +37 -0
- package/dist/context/onChange.js.map +1 -0
- package/dist/source-conformance.d.ts +1 -1
- package/dist/source-conformance.d.ts.map +1 -1
- package/dist/source-conformance.js +1 -1
- package/dist/source-conformance.js.map +1 -1
- package/dist/source-drizzle.d.ts +1 -1
- package/dist/source-drizzle.d.ts.map +1 -1
- package/dist/source-drizzle.js +1 -1
- package/dist/source-drizzle.js.map +1 -1
- package/dist/source-kysely.d.ts +1 -1
- package/dist/source-kysely.d.ts.map +1 -1
- package/dist/source-kysely.js +1 -1
- package/dist/source-kysely.js.map +1 -1
- package/dist/source-next.d.ts +1 -1
- package/dist/source-next.d.ts.map +1 -1
- package/dist/source-next.js +1 -1
- package/dist/source-next.js.map +1 -1
- package/docs/agent-integration-decision-guide.md +123 -0
- package/docs/agents.md +18 -14
- package/docs/api-keys.md +6 -6
- package/docs/api.md +52 -29
- package/docs/branch-development.md +23 -4
- package/docs/cli.md +16 -9
- package/docs/client-behavior.md +21 -15
- package/docs/concurrency-convention.md +67 -77
- package/docs/context.md +56 -31
- package/docs/coordination.md +115 -36
- package/docs/data-sources.md +12 -6
- package/docs/debugging.md +1 -1
- package/docs/examples/agent-human.md +6 -18
- package/docs/examples/coordination-conformance.md +69 -0
- package/docs/examples/existing-document-pipeline.md +488 -0
- package/docs/examples/existing-python-backend.md +10 -13
- package/docs/examples/nextjs.md +2 -2
- package/docs/examples/scoped-agent.md +18 -1
- package/docs/examples/server-agent.md +2 -2
- package/docs/groups.md +19 -139
- package/docs/guarantees.md +5 -6
- package/docs/identity.md +2 -1
- package/docs/index.md +5 -0
- package/docs/integration-guide.md +20 -19
- package/docs/integrations/sandbox-runtime.md +148 -0
- package/docs/integrations.md +9 -0
- package/docs/operating-on-your-database.md +7 -0
- package/docs/quickstart.md +19 -13
- package/docs/react.md +9 -9
- package/docs/schema-contract.md +14 -13
- package/docs/sessions.md +1 -1
- package/examples/README.md +2 -2
- package/examples/agent-turn.ts +1 -1
- package/examples/expensive-agent-turn.ts +1 -1
- package/llms.txt +22 -11
- package/package.json +6 -6
- package/dist/context/sources.d.ts +0 -21
- package/dist/context/sources.d.ts.map +0 -1
- package/dist/context/sources.js +0 -36
- package/dist/context/sources.js.map +0 -1
- package/dist/context.d.ts +0 -22
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -33
- package/dist/context.js.map +0 -1
package/docs/react.md
CHANGED
|
@@ -25,7 +25,7 @@ as props. Construct the client once, then pass that instance to the provider.
|
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
27
|
// lib/ablo.ts
|
|
28
|
-
import Ablo from '@abloatai/ablo';
|
|
28
|
+
import { Ablo } from '@abloatai/ablo/react';
|
|
29
29
|
import { createAbloReact } from '@abloatai/ablo/react';
|
|
30
30
|
import { schema } from '@/ablo/schema';
|
|
31
31
|
|
|
@@ -140,12 +140,12 @@ const reports = useAblo((ablo) =>
|
|
|
140
140
|
## Server Load
|
|
141
141
|
|
|
142
142
|
```tsx
|
|
143
|
-
const report = await ablo.weatherReports.
|
|
143
|
+
const report = await ablo.weatherReports.read({ id });
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Use `get` in Server Components when the row may not be in the local pool
|
|
147
147
|
yet — it hydrates from the local store and the server, and returns a Promise, so
|
|
148
|
-
`await` it. (Server reads come in two shapes: `
|
|
148
|
+
`await` it. (Server reads come in two shapes: `read({ id })` for one row and
|
|
149
149
|
`list({ where })` for many; both are async. The synchronous local reads are
|
|
150
150
|
the `local` reads, used in render below.)
|
|
151
151
|
|
|
@@ -156,12 +156,12 @@ For Server Actions and route handlers, call the SDK directly:
|
|
|
156
156
|
```ts
|
|
157
157
|
import { ablo } from '@/lib/ablo';
|
|
158
158
|
|
|
159
|
-
const
|
|
159
|
+
const report = await ablo.weatherReports.read({ id });
|
|
160
|
+
if (!report) throw new Error('report not found');
|
|
160
161
|
await ablo.weatherReports.update({
|
|
161
162
|
id,
|
|
162
163
|
data: patch,
|
|
163
|
-
|
|
164
|
-
onStale: 'reject',
|
|
164
|
+
reads: [report],
|
|
165
165
|
});
|
|
166
166
|
```
|
|
167
167
|
|
|
@@ -173,12 +173,12 @@ const ablo = useAblo();
|
|
|
173
173
|
|
|
174
174
|
async function markReady() {
|
|
175
175
|
if (!ablo) return;
|
|
176
|
-
const
|
|
176
|
+
const report = await ablo.weatherReports.read({ id });
|
|
177
|
+
if (!report) return;
|
|
177
178
|
await ablo.weatherReports.update({
|
|
178
179
|
id,
|
|
179
180
|
data: { status: 'ready' },
|
|
180
|
-
|
|
181
|
-
onStale: 'reject',
|
|
181
|
+
reads: [report],
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
184
|
```
|
package/docs/schema-contract.md
CHANGED
|
@@ -51,26 +51,27 @@ The model key (`weatherReports`) becomes the client namespace
|
|
|
51
51
|
contract. You should not create a parallel string-keyed write path for the same
|
|
52
52
|
data.
|
|
53
53
|
|
|
54
|
-
###
|
|
54
|
+
### The one field you don't declare
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
`id` is supplied on every row, so leave it out of your `model(...)` fields. That
|
|
57
|
+
is the whole list.
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
Two things look like framework territory and are not. **Audit fields are yours to
|
|
60
|
+
declare and yours to fill.** Add `createdAt`, `updatedAt` or `createdBy` and
|
|
61
|
+
`ablo migrate` gives each a column, which your own write or a database default
|
|
62
|
+
then populates; Ablo records who changed what in its transaction log and does not
|
|
63
|
+
write these columns for you. Omit them and no column is created at all; the model
|
|
64
|
+
still reads and writes, it just orders and attributes its history less precisely.
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
**The tenancy column** (`organizationId` by default) comes from the model's
|
|
67
|
+
`policy` rather than its field list, so you neither declare it nor lose it.
|
|
67
68
|
|
|
68
69
|
## Reads and writes
|
|
69
70
|
|
|
70
71
|
Use async reads when the row may not be local:
|
|
71
72
|
|
|
72
73
|
```ts
|
|
73
|
-
const report = await ablo.weatherReports.
|
|
74
|
+
const report = await ablo.weatherReports.read({ id: reportId });
|
|
74
75
|
const ready = await ablo.weatherReports.list({ where: { status: 'ready' } });
|
|
75
76
|
```
|
|
76
77
|
|
|
@@ -123,8 +124,8 @@ session route, never a raw API key.
|
|
|
123
124
|
- Keep direct database writes out of the coordinated path unless they are reported
|
|
124
125
|
back through Data Source events.
|
|
125
126
|
- Use `claim` for slow read -> think -> write spans.
|
|
126
|
-
- Use `
|
|
127
|
-
after it was read.
|
|
127
|
+
- Use `read` and pass its exact row in `reads` when a write must fail if the row
|
|
128
|
+
changed after it was read.
|
|
128
129
|
|
|
129
130
|
For the shortest runnable path, start with [Quickstart](./quickstart.md). For a
|
|
130
131
|
production app, continue with [Integration Guide](./integration-guide.md).
|
package/docs/sessions.md
CHANGED
package/examples/README.md
CHANGED
|
@@ -30,8 +30,8 @@ For read-reason-write work, pass the exact returned rows that informed the
|
|
|
30
30
|
decision. Their watermarks stay opaque:
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
|
-
const record = await ablo.records.
|
|
34
|
-
const policy = await ablo.policies.
|
|
33
|
+
const record = await ablo.records.read({ id: recordId });
|
|
34
|
+
const policy = await ablo.policies.read({ id: policyId });
|
|
35
35
|
const result = await model({ record, policy });
|
|
36
36
|
await ablo.records.update({
|
|
37
37
|
id: record.id,
|
package/examples/agent-turn.ts
CHANGED
|
@@ -21,7 +21,7 @@ if (!recordId) throw new Error('RECORD_ID is required');
|
|
|
21
21
|
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
22
22
|
try {
|
|
23
23
|
await ablo.ready();
|
|
24
|
-
const record = await ablo.records.
|
|
24
|
+
const record = await ablo.records.read({ id: recordId });
|
|
25
25
|
if (!record) throw new Error(`Record ${recordId} was not found`);
|
|
26
26
|
const commitId = `record:${recordId}:cheap`;
|
|
27
27
|
await ablo.records.update({
|
|
@@ -49,7 +49,7 @@ try {
|
|
|
49
49
|
if (!durable) throw new Error(`Commit ${commitId} was not retained`);
|
|
50
50
|
console.log({
|
|
51
51
|
identity: ablo.identity,
|
|
52
|
-
|
|
52
|
+
reads: durable.reads,
|
|
53
53
|
attempts: durable.attempts,
|
|
54
54
|
claims: durable.claims,
|
|
55
55
|
authority: durable.authority,
|
package/llms.txt
CHANGED
|
@@ -27,7 +27,7 @@ These are the jobs it is WRONG for, and saying so saves you a wasted integration
|
|
|
27
27
|
- **Vector search, embeddings, or retrieval.** Different problem. Ablo coordinates the rows; your vector store indexes them.
|
|
28
28
|
- **A file, blob, or artifact store.** Coordinate the ROW that points at the object; put the bytes in object storage.
|
|
29
29
|
|
|
30
|
-
How to call it, shortest path first: install `@abloatai/ablo`, run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`, and construct `Ablo({ schema, apiKey: process.env.ABLO_API_KEY })`.
|
|
30
|
+
How to call it, shortest path first: install `@abloatai/ablo`, run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla>`, and construct `Ablo({ schema, apiKey: process.env.ABLO_API_KEY })`. Use `ablo.<model>.get({ id })` to observe a row. Use `const input = await ablo.<model>.read({ id })`, then pass `reads: [input]` to the create/update/delete whose decision used it; if the row moved, that mutation does not land. `list({ where })` is observational. Wrap anything slow and exclusive in `await using claim = await ablo.<model>.claim({ id })`. The sections below cover the rest; "Start here" is the first thing to run.
|
|
31
31
|
|
|
32
32
|
## Machine-readable surfaces
|
|
33
33
|
|
|
@@ -38,10 +38,15 @@ Everything below is public, needs no credential, and is served from `https://www
|
|
|
38
38
|
- `/developers` — every developer surface named on one page: the SDK, the API reference, the OpenAPI document, the MCP server, the CLI.
|
|
39
39
|
- `/openapi.json` — the REST contract as OpenAPI 3.1: every route, a stable `operationId` and description on each, typed responses, and the `ErrorEnvelope` every 4xx and 5xx decodes through. Generate a client from it when no SDK exists for your runtime.
|
|
40
40
|
- `/mcp` — the integration-helper MCP server over Streamable HTTP. POST your JSON-RPC here; a GET returns a descriptor rather than the protocol.
|
|
41
|
+
- `/.well-known/mcp` — the same Streamable HTTP server at its direct discovery endpoint, with no page/transport negotiation.
|
|
41
42
|
- `/.well-known/mcp.json` — that server's manifest, in the MCP registry's `server.json` format.
|
|
42
43
|
- `/api/docs/<page>` — any documentation page as plain Markdown, for a client that fetches URLs rather than speaking MCP.
|
|
43
44
|
- `/sitemap.xml` — every indexable page on the domain.
|
|
44
45
|
|
|
46
|
+
Pricing and plan limits are public at [Ablo pricing](https://docs.abloatai.com/pricing). Do not
|
|
47
|
+
infer prices or follow a dashboard billing link: the documentation page is the canonical rate
|
|
48
|
+
card and needs no sign-in.
|
|
49
|
+
|
|
45
50
|
Every page on that host serves a Markdown representation from its own URL: send
|
|
46
51
|
`Accept: text/markdown` (q-values are honoured), or append `.md` to the path if
|
|
47
52
|
your client cannot set the header. Responses carry `Vary: Accept`, a client that
|
|
@@ -65,6 +70,7 @@ Every surface reaches the same coordinated state. They are not interchangeable.
|
|
|
65
70
|
- Coordination MCP, `@abloatai/mcp` — an agent living inside an MCP host (Claude, Cursor). Two jobs: MANAGING your Ablo the way the CLI does (`get_schema`, `list_projects`, `create_project`, `tail_logs`, `get_usage`), and claim/commit as tools over your rows. Call `get_schema` first — it is the only way to learn the model names every data tool needs. `init`, `push`, `pull`, `generate` have no tools (they touch your repo); run those in a shell.
|
|
66
71
|
- `humans()` with `@abloatai/humans/react` — the interfaces a person watches agent work arrive in. The bare client is the coordination layer (commit, read, observe, claim); `humans()` is the plugin that adds the local watchable copy, live queries, and presence. It `requires: { duplex: true }`, so a `transport: 'http'` agent is rejected at construction rather than left holding a subscription that never delivers. It remains the compatibility default for omitted `plugins`; new code should install it explicitly from `@abloatai/humans`. There is no `agents()` plugin — agents are the default caller, not a special one. A browser NEVER receives the secret key; mint a session token.
|
|
67
72
|
- CLI, `ablo` — scaffolding, schema push, connecting a database. Terminals and CI. Agents must run it non-interactively (see the CLI section below).
|
|
73
|
+
- CLI package, [`@abloatai/cli`](https://www.npmjs.com/package/@abloatai/cli) — the official npm package that installs the `ablo` binary.
|
|
68
74
|
- REST, `/api/v1` — runtimes with no SDK.
|
|
69
75
|
- Integration-helper MCP, hosted `/api/mcp` — teaching a coding assistant the SDK WHILE BUILDING an integration. Docs, schema lint, and scaffolds only; it has NO per-model data tools and cannot touch application rows.
|
|
70
76
|
|
|
@@ -103,7 +109,7 @@ const schema = defineSchema({
|
|
|
103
109
|
|
|
104
110
|
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
|
|
105
111
|
|
|
106
|
-
const report = await ablo.weatherReports.
|
|
112
|
+
const report = await ablo.weatherReports.read({ id: 'report_stockholm' });
|
|
107
113
|
if (!report) throw new Error('Row not found');
|
|
108
114
|
|
|
109
115
|
// Claim the row (waits if someone else holds it), read the fresh copy off
|
|
@@ -115,7 +121,7 @@ const updated = await ablo.weatherReports.update({
|
|
|
115
121
|
});
|
|
116
122
|
```
|
|
117
123
|
|
|
118
|
-
That is the normal app path: declare models in a schema, then use `ablo.<model>.
|
|
124
|
+
That is the normal app path: declare models in a schema, then use `ablo.<model>.read({ id })`, `ablo.<model>.create(...)`, `ablo.<model>.update(...)`, and `ablo.<model>.delete(...)`. Every verb takes a single options object.
|
|
119
125
|
|
|
120
126
|
Treat the schema as the integration contract. It drives typed model clients,
|
|
121
127
|
React selectors, server and agent writes, Data Source request/response shape,
|
|
@@ -126,12 +132,12 @@ For full integrations, use `integration-guide` as the canonical doc. It covers
|
|
|
126
132
|
the same model API across your own Data Source-backed app databases,
|
|
127
133
|
React selectors, multiplayer, and future agent workers.
|
|
128
134
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
Server reads separate observation from decision input. `get({ id })` and
|
|
136
|
+
`list({ where })` return current data without creating stale evidence.
|
|
137
|
+
`read({ id })` returns the same row with private `{ model, id, readAt }` evidence
|
|
138
|
+
for a specific mutation's `reads` array. `local.get(id)`,
|
|
132
139
|
`local.list({ where })`, and `local.count({ where })` are the same reads narrowed
|
|
133
|
-
to what has already synced: nothing to await, reactive in render.
|
|
134
|
-
second verb to learn — `local.` is the only difference. The query reads accept
|
|
140
|
+
to what has already synced: nothing to await, reactive in render. The collection queries accept
|
|
135
141
|
`where`, `filter`, `orderBy`, `limit`, `offset`,
|
|
136
142
|
and `state`; state defaults to `'live'`, with `'archived'` and `'all'` to include
|
|
137
143
|
retired rows.
|
|
@@ -168,7 +174,7 @@ coordination until the app reports it through Data Source events.
|
|
|
168
174
|
|
|
169
175
|
## Change propagation
|
|
170
176
|
|
|
171
|
-
A change to one row reaches other rows three ways. ROUTING: a write fans out to every sync group the row belongs to, INCLUDING its ancestors' groups (editing a block routes to `block:` + `document:` + `workspace:`), so everyone watching the workspace sees it — delivery, not recomputation. DELETE CASCADE: deleting a parent emits explicit tombstone deltas for its descendants, so open clients never silently hold rows that are gone. VALUE: derived values are NOT recomputed server-side — Ablo surfaces that the source moved and the actor decides. To keep dependent work fresh,
|
|
177
|
+
A change to one row reaches other rows three ways. ROUTING: a write fans out to every sync group the row belongs to, INCLUDING its ancestors' groups (editing a block routes to `block:` + `document:` + `workspace:`), so everyone watching the workspace sees it — delivery, not recomputation. DELETE CASCADE: deleting a parent emits explicit tombstone deltas for its descendants, so open clients never silently hold rows that are gone. VALUE: derived values are NOT recomputed server-side — Ablo surfaces that the source moved and the actor decides. To keep dependent work fresh, pass rows returned by `ablo.<model>.read({ id })` in the mutation's `reads` array. Ablo records compact model/id/readAt evidence, not row contents. At commit the server checks whether anything moved past the read watermark; if so, the mutation does not land. Use `get` when no such relationship exists. To chain A→B→C, put A+B in one group and B+C in another: A's change reaches B, and C hears it only once B ITSELF writes. No transitive auto-recompute, no convergence guarantee for cycles.
|
|
172
178
|
|
|
173
179
|
## Nouns
|
|
174
180
|
|
|
@@ -180,7 +186,7 @@ A change to one row reaches other rows three ways. ROUTING: a write fans out to
|
|
|
180
186
|
## Claimed Behavior
|
|
181
187
|
|
|
182
188
|
Reads never silently block. Schema reads stay open while a row is claimed.
|
|
183
|
-
Typed reads through `ablo.<model>.
|
|
189
|
+
Typed reads through `ablo.<model>.read({ id })` return the row by default,
|
|
184
190
|
even while it is claimed. Pass `ifClaimed: 'fail'` to throw
|
|
185
191
|
`AbloClaimedError`, and inspect active coordination separately through
|
|
186
192
|
`ablo.<model>.claim.state({ id })`.
|
|
@@ -198,7 +204,7 @@ frees. Use `ifClaimed: 'fail'` when you'd rather refuse to read a claimed row.
|
|
|
198
204
|
|
|
199
205
|
## Guarantees
|
|
200
206
|
|
|
201
|
-
Schema model writes update local state optimistically, while their returned promise always waits for authoritative confirmation. Server rejection rolls back local state. To prevent lost updates,
|
|
207
|
+
Schema model writes update local state optimistically, while their returned promise always waits for authoritative confirmation. Server rejection rolls back local state. To prevent lost updates, fetch the decision input with `read({ id })` and pass that exact row in the mutation's `reads` array. If it changed, the mutation does not land.
|
|
202
208
|
|
|
203
209
|
Claims coordinate writers; they do not block readers. Most users should stay on
|
|
204
210
|
schema-backed reads/writes and `claim(...)`; manual protocol bookkeeping is not
|
|
@@ -282,6 +288,11 @@ Import from these public paths only:
|
|
|
282
288
|
|
|
283
289
|
Do not teach `/api`, `/agent`, `/core`, `/realtime`, or internal subpaths. (`/source` and `/ai-sdk` are public.)
|
|
284
290
|
|
|
291
|
+
`onChange` is WebSocket-only. It exists on the reactive client because that
|
|
292
|
+
client holds an open socket and a local graph. The stateless HTTP client does
|
|
293
|
+
not expose it at the type or runtime level; HTTP agents re-read or inspect
|
|
294
|
+
durable logs instead.
|
|
295
|
+
|
|
285
296
|
## CLI: agents run it NON-INTERACTIVELY
|
|
286
297
|
|
|
287
298
|
`ablo init` and other prompts need a TTY; an agent/CI run has none and will HANG. Always:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "The public Ablo SDK for coordinated reads, commits, claims, observation, and reactive applications.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -81,9 +81,9 @@
|
|
|
81
81
|
"default": "./dist/ai-sdk.js"
|
|
82
82
|
},
|
|
83
83
|
"./context": {
|
|
84
|
-
"types": "./dist/context.d.ts",
|
|
85
|
-
"import": "./dist/context.js",
|
|
86
|
-
"default": "./dist/context.js"
|
|
84
|
+
"types": "./dist/context/index.d.ts",
|
|
85
|
+
"import": "./dist/context/index.js",
|
|
86
|
+
"default": "./dist/context/index.js"
|
|
87
87
|
},
|
|
88
88
|
"./wire": {
|
|
89
89
|
"types": "./dist/wire.d.ts",
|
|
@@ -139,8 +139,8 @@
|
|
|
139
139
|
"directory": "packages/ablo"
|
|
140
140
|
},
|
|
141
141
|
"dependencies": {
|
|
142
|
-
"@abloatai/humans": "^0.
|
|
143
|
-
"@abloatai/transaction": "^0.
|
|
142
|
+
"@abloatai/humans": "^0.58.0",
|
|
143
|
+
"@abloatai/transaction": "^0.58.0",
|
|
144
144
|
"zod": "^4.4.3"
|
|
145
145
|
},
|
|
146
146
|
"peerDependencies": {
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import type { ContextEvidenceSlice } from './evidence.js';
|
|
3
|
-
export declare const contextSourceSchema: z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4
|
-
key: z.ZodString;
|
|
5
|
-
kind: z.ZodLiteral<"ablo">;
|
|
6
|
-
guarantee: z.ZodLiteral<"guardable">;
|
|
7
|
-
cursor: z.ZodNumber;
|
|
8
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
9
|
-
key: z.ZodString;
|
|
10
|
-
kind: z.ZodLiteral<"value">;
|
|
11
|
-
guarantee: z.ZodLiteral<"informational">;
|
|
12
|
-
cursor: z.ZodNull;
|
|
13
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
14
|
-
key: z.ZodString;
|
|
15
|
-
kind: z.ZodLiteral<"mixed">;
|
|
16
|
-
guarantee: z.ZodLiteral<"partial">;
|
|
17
|
-
cursor: z.ZodNumber;
|
|
18
|
-
}, z.core.$strip>], "kind">>;
|
|
19
|
-
export type ContextSource = z.infer<typeof contextSourceSchema>;
|
|
20
|
-
export declare function sourceFor(key: string, evidence: ContextEvidenceSlice): ContextSource;
|
|
21
|
-
//# sourceMappingURL=sources.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;4BAmBnB,CAAC;AAEd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,oBAAoB,GAC7B,aAAa,CAaf"}
|
package/dist/context/sources.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export const contextSourceSchema = z.discriminatedUnion('kind', [
|
|
3
|
-
z.object({
|
|
4
|
-
key: z.string(),
|
|
5
|
-
kind: z.literal('ablo'),
|
|
6
|
-
guarantee: z.literal('guardable'),
|
|
7
|
-
cursor: z.number().int().nonnegative(),
|
|
8
|
-
}),
|
|
9
|
-
z.object({
|
|
10
|
-
key: z.string(),
|
|
11
|
-
kind: z.literal('value'),
|
|
12
|
-
guarantee: z.literal('informational'),
|
|
13
|
-
cursor: z.null(),
|
|
14
|
-
}),
|
|
15
|
-
z.object({
|
|
16
|
-
key: z.string(),
|
|
17
|
-
kind: z.literal('mixed'),
|
|
18
|
-
guarantee: z.literal('partial'),
|
|
19
|
-
cursor: z.number().int().nonnegative(),
|
|
20
|
-
}),
|
|
21
|
-
]).readonly();
|
|
22
|
-
export function sourceFor(key, evidence) {
|
|
23
|
-
if (evidence.reads.length === 0) {
|
|
24
|
-
return contextSourceSchema.parse({
|
|
25
|
-
key,
|
|
26
|
-
kind: 'value',
|
|
27
|
-
guarantee: 'informational',
|
|
28
|
-
cursor: null,
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
const cursor = Math.max(...evidence.reads.map((item) => item.entry.watermark));
|
|
32
|
-
return evidence.includesInformational
|
|
33
|
-
? contextSourceSchema.parse({ key, kind: 'mixed', guarantee: 'partial', cursor })
|
|
34
|
-
: contextSourceSchema.parse({ key, kind: 'ablo', guarantee: 'guardable', cursor });
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=sources.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC9D,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACrC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE;KACjB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;CACH,CAAC,CAAC,QAAQ,EAAE,CAAC;AAId,MAAM,UAAU,SAAS,CACvB,GAAW,EACX,QAA8B;IAE9B,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,mBAAmB,CAAC,KAAK,CAAC;YAC/B,GAAG;YACH,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,eAAe;YAC1B,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/E,OAAO,QAAQ,CAAC,qBAAqB;QACnC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACjF,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC"}
|
package/dist/context.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { CapturedRow } from '@abloatai/transaction';
|
|
2
|
-
import { type AwaitedDeep } from './context/await.js';
|
|
3
|
-
import { type ContextSource } from './context/sources.js';
|
|
4
|
-
export { contextSourceSchema, type ContextSource } from './context/sources.js';
|
|
5
|
-
export type { AwaitedDeep } from './context/await.js';
|
|
6
|
-
export interface ContextOptions<TData extends Readonly<Record<string, unknown>>> {
|
|
7
|
-
/** The client whose read evidence may guard a later write. */
|
|
8
|
-
readonly ablo: object;
|
|
9
|
-
/** Values selected by the application. Nested promises are accepted. */
|
|
10
|
-
readonly data: TData;
|
|
11
|
-
}
|
|
12
|
-
export interface ContextResult<TData extends Readonly<Record<string, unknown>>> {
|
|
13
|
-
readonly data: AwaitedDeep<TData>;
|
|
14
|
-
/** Exact returned Ablo rows, ready to pass to a write's `reads` option. */
|
|
15
|
-
readonly reads: readonly CapturedRow[];
|
|
16
|
-
/** The greatest watermark among included authoritative reads. */
|
|
17
|
-
readonly cursor: number | null;
|
|
18
|
-
readonly sources: readonly ContextSource[];
|
|
19
|
-
}
|
|
20
|
-
/** Assemble selected application values and the Ablo evidence they retain. */
|
|
21
|
-
export declare function context<const TData extends Readonly<Record<string, unknown>>>(options: ContextOptions<TData>): Promise<ContextResult<TData>>;
|
|
22
|
-
//# sourceMappingURL=context.d.ts.map
|
package/dist/context.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7E,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,KAAK,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACjF,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAC7B,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAsB/B"}
|
package/dist/context.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Additive context assembly for one Ablo client.
|
|
3
|
-
*
|
|
4
|
-
* The caller chooses the data. This module awaits it, reports the exact Ablo
|
|
5
|
-
* rows it contains, and leaves model execution and external retrieval alone.
|
|
6
|
-
*/
|
|
7
|
-
import { z } from 'zod';
|
|
8
|
-
import { awaitDeep } from './context/await.js';
|
|
9
|
-
import { bindContextEvidence } from './context/evidence.js';
|
|
10
|
-
import { sourceFor } from './context/sources.js';
|
|
11
|
-
export { contextSourceSchema } from './context/sources.js';
|
|
12
|
-
const contextDataSchema = z.record(z.string(), z.unknown());
|
|
13
|
-
/** Assemble selected application values and the Ablo evidence they retain. */
|
|
14
|
-
export async function context(options) {
|
|
15
|
-
const collectEvidence = bindContextEvidence(options.ablo);
|
|
16
|
-
const data = await awaitDeep(options.data);
|
|
17
|
-
const parsed = contextDataSchema.safeParse(data);
|
|
18
|
-
if (!parsed.success) {
|
|
19
|
-
throw new TypeError('context() requires `data` to be an object.', { cause: parsed.error });
|
|
20
|
-
}
|
|
21
|
-
const evidence = collectEvidence(data);
|
|
22
|
-
const sources = Object.entries(data).map(([key, value]) => sourceFor(key, evidence.inspect(value)));
|
|
23
|
-
const cursor = evidence.all.length === 0
|
|
24
|
-
? null
|
|
25
|
-
: Math.max(...evidence.all.map((item) => item.entry.watermark));
|
|
26
|
-
return {
|
|
27
|
-
data: data,
|
|
28
|
-
reads: evidence.all.map((item) => item.row),
|
|
29
|
-
cursor,
|
|
30
|
-
sources,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=context.js.map
|
package/dist/context.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAsB,MAAM,sBAAsB,CAAC;AAG/E,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAkB5D,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAA8B;IAE9B,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACxD,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACxC,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC;QACtC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,IAAI,EAAE,IAA0B;QAChC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAkB,CAAC;QAC1D,MAAM;QACN,OAAO;KACR,CAAC;AACJ,CAAC"}
|