@abloatai/ablo 0.46.0 → 0.48.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 +1 -1
- package/CHANGELOG.md +160 -0
- package/LICENSE +1 -1
- package/NOTICE +3 -3
- package/README.md +20 -2
- package/docs/agent-messaging.md +0 -1
- package/docs/agents.md +1 -1
- package/docs/api.md +6 -5
- package/docs/cli.md +13 -5
- package/docs/client-behavior.md +8 -7
- package/docs/coordination.md +2 -2
- package/docs/data-sources.md +158 -15
- package/docs/debugging.md +4 -5
- package/docs/examples/agent-human.md +0 -2
- package/docs/examples/existing-python-backend.md +0 -2
- package/docs/examples/nextjs.md +1 -2
- package/docs/examples/server-agent.md +2 -3
- package/docs/groups.md +1 -1
- package/docs/guarantees.md +4 -7
- package/docs/how-it-works.md +6 -6
- package/docs/idempotency.md +0 -1
- package/docs/identity.md +1 -1
- package/docs/index.md +1 -2
- package/docs/integration-guide.md +7 -10
- package/docs/integrations/inngest.md +1 -4
- package/docs/integrations/temporal.md +1 -3
- package/docs/internal/serializable-schema.md +1 -1
- package/docs/internal/structure.md +4 -0
- package/docs/migration.md +3 -3
- package/docs/operating-on-your-database.md +2 -2
- package/docs/react.md +1 -3
- package/docs/schema-contract.md +2 -2
- package/examples/README.md +1 -1
- package/llms.txt +2 -3
- package/package.json +3 -3
- package/docs/internal/agent-orchestration.md +0 -58
package/AGENTS.md
CHANGED
|
@@ -79,7 +79,7 @@ await ablo.weatherReports.update({
|
|
|
79
79
|
|
|
80
80
|
## Coordination surface
|
|
81
81
|
|
|
82
|
-
Claims live on a callable namespace beside `create` / `update` / `
|
|
82
|
+
Claims live on a callable namespace beside `create` / `update` / `get`. Every member takes an options object:
|
|
83
83
|
|
|
84
84
|
- `await using claim = await ablo.<model>.claim({ id })` — acquire the row (waits if held); read it via `claim.data`; auto-releases on scope exit (or call `claim.release()`).
|
|
85
85
|
- `ablo.<model>.claim.state({ id })` — who is currently working on the row (synchronous; never blocks).
|
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,154 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.48.0
|
|
4
|
+
|
|
5
|
+
### A branch is unbound until you connect a database to it
|
|
6
|
+
|
|
7
|
+
A branch keeps its own storage, and it does not quietly get Ablo's. Until a
|
|
8
|
+
database is connected to that branch, a request needing one fails with
|
|
9
|
+
`no_data_source_registered` and says what to do:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
This branch is not connected to your database yet.
|
|
13
|
+
Run `ablo connect` for this branch, then retry.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The old `test_database_not_registered` is gone. It described a sandbox that no
|
|
17
|
+
longer exists, it arrived on requests that had nothing to do with a test
|
|
18
|
+
database, and its advice pointed at options the SDK had already removed. A
|
|
19
|
+
branch created by `ablo dev` now reports its state plainly rather than looking
|
|
20
|
+
ready and then refusing the first schema push.
|
|
21
|
+
|
|
22
|
+
**Action required.** Replace any handler matching `test_database_not_registered`
|
|
23
|
+
with `no_data_source_registered`. The new code carries the same 4xx meaning and
|
|
24
|
+
a recovery path a caller can act on.
|
|
25
|
+
|
|
26
|
+
### Failures say which branch they happened on
|
|
27
|
+
|
|
28
|
+
An unconnected branch previously returned its refusal with nothing written
|
|
29
|
+
server-side, so a support question about one customer's branch could not be
|
|
30
|
+
answered from logs at all. These now carry the request, organization, project,
|
|
31
|
+
branch, key kind and storage state, indexed in Sentry, so a failure can be
|
|
32
|
+
looked up by branch instead of reconstructed from database tables.
|
|
33
|
+
|
|
34
|
+
### Replication uses your branch's own publication and slot
|
|
35
|
+
|
|
36
|
+
Registration, validation, writer checks, replication, drift detection and
|
|
37
|
+
`ablo connect` all require the branch-scoped names recorded when the source was
|
|
38
|
+
registered. Nothing falls back to a shared `ablo_publication` or `ablo_slot`
|
|
39
|
+
any more, so two branches on one database can never quietly share a stream.
|
|
40
|
+
`ablo connect scan` reports legacy unsuffixed objects as retired.
|
|
41
|
+
|
|
42
|
+
Once your engine is on this release and `ablo connect check` is clean, the
|
|
43
|
+
temporary alias can go:
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
DROP PUBLICATION IF EXISTS "ablo_publication";
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Renamed
|
|
50
|
+
|
|
51
|
+
`FootprintPlane` is now `DataSourceIdentity`, with the same three fields. The
|
|
52
|
+
old name described an internal layout; the new one describes what it
|
|
53
|
+
identifies.
|
|
54
|
+
|
|
55
|
+
## 0.47.0
|
|
56
|
+
|
|
57
|
+
### Local Postgres works with Ablo Cloud
|
|
58
|
+
|
|
59
|
+
Run `npx ablo dev --local` to serve the generated signed Data Source handler
|
|
60
|
+
over an outbound, protocol-scoped connector. Postgres remains private on the
|
|
61
|
+
developer's machine and its connection string never leaves the app process.
|
|
62
|
+
The Data Source guide now explains exactly which writes are visible without
|
|
63
|
+
WAL, and the public error reference includes actionable `source_connector_*`
|
|
64
|
+
codes for every connector lifecycle failure.
|
|
65
|
+
|
|
66
|
+
### Awaiting a model write now means it is confirmed
|
|
67
|
+
|
|
68
|
+
`create`, `update`, and `delete` change local reactive state immediately and
|
|
69
|
+
return a promise with a single meaning: the write reached authoritative
|
|
70
|
+
confirmation. An interface stays responsive without awaiting anything, and code
|
|
71
|
+
that needs to know a write survived can await the same call it already makes.
|
|
72
|
+
|
|
73
|
+
The `wait` option is gone from the client and from individual model calls.
|
|
74
|
+
Awaiting a model write always waits for confirmation, so there is nothing left
|
|
75
|
+
to configure. Explicit control over a queued versus confirmed receipt remains on
|
|
76
|
+
`commits.create`, which still hands back the receipt and its confirmation
|
|
77
|
+
separately.
|
|
78
|
+
|
|
79
|
+
**Action required.** Remove `wait` from `Ablo({ ... })` and from every
|
|
80
|
+
`create`, `update`, and `delete` call.
|
|
81
|
+
|
|
82
|
+
- `wait: 'confirmed'` behaves identically once removed.
|
|
83
|
+
- `wait: 'queued'` on a call you never awaited behaves identically once removed.
|
|
84
|
+
- `wait: 'queued'` on a call you did await now waits for confirmation. Move to
|
|
85
|
+
`commits.create` if the queued receipt was the reason for the option.
|
|
86
|
+
|
|
87
|
+
### Customer branches connect before accepting a schema
|
|
88
|
+
|
|
89
|
+
A customer branch now remains in provisioning until it has an active Data
|
|
90
|
+
Source. Ablo does not invent internal storage for customer data: it reads the
|
|
91
|
+
customer's database through WAL and writes through the separately scoped DML
|
|
92
|
+
credential (or uses the explicitly registered signed endpoint fallback).
|
|
93
|
+
|
|
94
|
+
Database validation now uses the branch-scoped publication and replication slot
|
|
95
|
+
persisted with that Data Source. It no longer falls back to the shared
|
|
96
|
+
`ablo_publication` / `ablo_slot` names, so `ablo connect check` validates the
|
|
97
|
+
same objects that `ablo connect apply` created.
|
|
98
|
+
|
|
99
|
+
The sandbox-only `test_database_not_registered` error has been removed. An
|
|
100
|
+
unconnected customer branch now consistently returns `no_data_source_registered`
|
|
101
|
+
with the `ablo connect` recovery step.
|
|
102
|
+
|
|
103
|
+
**Action required for type imports.** `FootprintPlane` has been removed. Import
|
|
104
|
+
`DataSourceIdentity` from `@abloatai/ablo/source` instead; its fields remain
|
|
105
|
+
`organizationId`, optional `projectId`, and `branchId`.
|
|
106
|
+
|
|
107
|
+
### The CLI names the problem it actually hit
|
|
108
|
+
|
|
109
|
+
A refused push no longer reports every failure as a missing `schema:push`
|
|
110
|
+
capability. That advice was wrong for most refusals: a database privilege error,
|
|
111
|
+
a row-level security misconfiguration, and an unregistered development database
|
|
112
|
+
each need a different fix, and none of them is a different API key. Each now
|
|
113
|
+
leads with the server's own message and the remedy for that specific cause.
|
|
114
|
+
|
|
115
|
+
Project names also resolve correctly under a branch-bound key. Listing projects
|
|
116
|
+
is a management operation that such a key is deliberately not allowed to
|
|
117
|
+
perform, so `ablo status` and `ablo push` reported a correctly minted key's
|
|
118
|
+
project as `unnamed` alongside a permission error. The name now comes from the
|
|
119
|
+
stored management credential.
|
|
120
|
+
|
|
121
|
+
### Deprecations
|
|
122
|
+
|
|
123
|
+
`METER_EVENT_COUNTS` is deprecated in favour of its per-surface members, and the
|
|
124
|
+
`DatasourceResnapshotResponse` type and its schema are deprecated. All three
|
|
125
|
+
still ship and still work; they will be removed in a later release.
|
|
126
|
+
|
|
3
127
|
## 0.46.0
|
|
4
128
|
|
|
129
|
+
### Provider branches isolate environments; schemas isolate projects
|
|
130
|
+
|
|
131
|
+
Several Ablo projects can now share one physical Postgres database safely when
|
|
132
|
+
each owns a separate schema. `ablo connect apply --schema mail` binds the
|
|
133
|
+
authenticated project branch to `(database, mail)` and derives an independent
|
|
134
|
+
slot, publication, replication role, and writer role. Publications enumerate
|
|
135
|
+
schema-qualified mapped tables and the idempotency ledger lives in the selected
|
|
136
|
+
schema. A provider branch's distinct direct URL remains the environment boundary
|
|
137
|
+
for Neon, Supabase, and similar hosts.
|
|
138
|
+
|
|
139
|
+
Registration enforces one owner per `(database, schema)` globally, without
|
|
140
|
+
revealing another organization's coordinates, and refuses a new binding when
|
|
141
|
+
`max_replication_slots` has no capacity. Runtime replication, rotate,
|
|
142
|
+
resnapshot, disconnect, and scan use the binding's stored/derived footprint;
|
|
143
|
+
legacy manual setups remain single-binding.
|
|
144
|
+
|
|
145
|
+
New clients can pin their intended project and branch with `projectId` /
|
|
146
|
+
`ABLO_PROJECT_ID` and `branchId` / `ABLO_BRANCH_ID`. `ablo dev` writes both
|
|
147
|
+
immutable coordinates beside its branch-bound key, and `ready()` compares them
|
|
148
|
+
with the key's server-resolved target before opening the sync connection. A mail
|
|
149
|
+
deployment carrying a slides key now fails with `project_scope_denied`; a
|
|
150
|
+
same-project key for the wrong environment fails with `branch_scope_denied`.
|
|
151
|
+
|
|
5
152
|
### Pre-existing rows arrive on their own
|
|
6
153
|
|
|
7
154
|
Connecting a database that already holds data no longer leaves those rows
|
|
@@ -13,6 +160,19 @@ snapshot completes, and `ablo status --json` exposes the progress as
|
|
|
13
160
|
`complete`. Row-touch backfill scripts are unnecessary; the snapshot is the
|
|
14
161
|
engine's job.
|
|
15
162
|
|
|
163
|
+
The snapshot reader now detects row-level security that would filter its
|
|
164
|
+
ordinary `SELECT` even though WAL carries every published row. New setup plans
|
|
165
|
+
give the read-only replication role `BYPASSRLS` (with `SELECT` still restricted
|
|
166
|
+
to published tables), and readiness refuses to call an RLS-filtered snapshot
|
|
167
|
+
safe. Existing connections can run `ablo connect resnapshot` after repairing
|
|
168
|
+
the role; it recreates only the slot and keeps the DataSource and credentials.
|
|
169
|
+
The same completion guard rejects an empty or partial snapshot mapping: if a
|
|
170
|
+
pushed model's table is absent from the publication, completion stays pending
|
|
171
|
+
until coverage is repaired and a fresh snapshot is requested. Snapshot and WAL
|
|
172
|
+
mapping now use the DataSource's configured Postgres schema as part of relation
|
|
173
|
+
identity, so an identically named table in another schema cannot be loaded into
|
|
174
|
+
the model or falsely satisfy publication coverage.
|
|
175
|
+
|
|
16
176
|
## 0.45.0
|
|
17
177
|
|
|
18
178
|
### Claim admission is authoritative
|
package/LICENSE
CHANGED
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
|
187
187
|
identification within third-party archives.
|
|
188
188
|
|
|
189
|
-
Copyright 2025-2026
|
|
189
|
+
Copyright 2025-2026 Lukas Andersson
|
|
190
190
|
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
192
|
you may not use this file except in compliance with the License.
|
package/NOTICE
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
@ablo/ablo
|
|
2
|
-
Copyright 2025-2026
|
|
2
|
+
Copyright 2025-2026 Lukas Andersson
|
|
3
3
|
|
|
4
|
-
This product includes software developed by
|
|
4
|
+
This product includes software developed by Lukas Andersson
|
|
5
5
|
(https://ablo.finance).
|
|
6
6
|
|
|
7
|
-
"Ablo" is a trademark of
|
|
7
|
+
"Ablo" is a trademark of Lukas Andersson. This license does not grant
|
|
8
8
|
permission to use the Ablo name, logo, or trademarks. Third parties
|
|
9
9
|
may describe their use of or compatibility with Ablo factually (e.g.,
|
|
10
10
|
"built with @ablo/ablo") but may not use the Ablo name in a way
|
package/README.md
CHANGED
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
+
> **Reading the implementation?** Start with the
|
|
26
|
+
> **[source code map](./CODEMAP.md)**. It shows which files own `create`,
|
|
27
|
+
> `update`, `delete`, `claim`, schemas, transports, and the reactive client.
|
|
28
|
+
|
|
25
29
|
Safely coordinate AI agents, humans, workflows, and services writing to the
|
|
26
30
|
same database.
|
|
27
31
|
|
|
@@ -63,7 +67,6 @@ if (!order) throw new Error('Order not found');
|
|
|
63
67
|
await ablo.orders.update({
|
|
64
68
|
id: order.id,
|
|
65
69
|
data: { status: 'approved' },
|
|
66
|
-
wait: 'confirmed',
|
|
67
70
|
});
|
|
68
71
|
```
|
|
69
72
|
|
|
@@ -82,7 +85,6 @@ await ablo.orders.update({
|
|
|
82
85
|
id: claim.data.id,
|
|
83
86
|
data: { total: priced.total, status: 'repriced' },
|
|
84
87
|
claim,
|
|
85
|
-
wait: 'confirmed',
|
|
86
88
|
});
|
|
87
89
|
```
|
|
88
90
|
|
|
@@ -116,6 +118,22 @@ authority, commits, claims, and ordered changes.
|
|
|
116
118
|
Read the [Quickstart](https://docs.abloatai.com/quickstart), browse
|
|
117
119
|
[docs.abloatai.com](https://docs.abloatai.com), or run `npx ablo docs`.
|
|
118
120
|
|
|
121
|
+
## Navigating the source
|
|
122
|
+
|
|
123
|
+
This repository preserves the package ownership boundaries instead of
|
|
124
|
+
flattening the implementation into `packages/ablo`:
|
|
125
|
+
|
|
126
|
+
- `packages/ablo` is the branded public facade. Its files mostly re-export the
|
|
127
|
+
package that owns each API.
|
|
128
|
+
- `packages/transaction` owns the shared model-operation contracts and the
|
|
129
|
+
stateless HTTP implementation.
|
|
130
|
+
- `packages/humans` owns the reactive WebSocket/local/React implementation.
|
|
131
|
+
|
|
132
|
+
That means searching only inside `packages/ablo/src` will not find the
|
|
133
|
+
implementation of `create`, `update`, `delete`, or `claim`. Read the
|
|
134
|
+
**[source code map](./CODEMAP.md)** for a verb-by-verb ownership table and
|
|
135
|
+
guided call traces for both the default and reactive clients.
|
|
136
|
+
|
|
119
137
|
## Contributing
|
|
120
138
|
|
|
121
139
|
Ablo is free and open source. You can help by
|
package/docs/agent-messaging.md
CHANGED
package/docs/agents.md
CHANGED
|
@@ -147,7 +147,7 @@ default caller here, not a bolt-on.
|
|
|
147
147
|
|
|
148
148
|
```text
|
|
149
149
|
something happens ──▶ your agent (HTTP, no socket)
|
|
150
|
-
(a job, a webhook, read context (list/
|
|
150
|
+
(a job, a webhook, read context (list/get)
|
|
151
151
|
a queue message) claim → work → commit
|
|
152
152
|
done — no held connection
|
|
153
153
|
```
|
package/docs/api.md
CHANGED
|
@@ -41,7 +41,7 @@ await ablo.ready();
|
|
|
41
41
|
const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
|
|
42
42
|
if (!report) throw new Error('Row not found');
|
|
43
43
|
|
|
44
|
-
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' }
|
|
44
|
+
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' } });
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
For end-to-end app setup across React, existing backends, Data Source, and
|
|
@@ -75,7 +75,7 @@ fallback removed — nothing to await, so they return a value.
|
|
|
75
75
|
| `update({ id, data, ...options })` | `Promise<T>` | You want to update through the schema model. |
|
|
76
76
|
| `delete({ id, ...options })` | `Promise<void>` | You want to delete through the schema model. |
|
|
77
77
|
|
|
78
|
-
`
|
|
78
|
+
`get`, `list`, `create`, `update`, and `delete` are the main path — they go
|
|
79
79
|
through the server. The `local` reads work off the rows a session has already
|
|
80
80
|
synced, so a cheap re-read needs no round-trip.
|
|
81
81
|
|
|
@@ -91,17 +91,18 @@ await ablo.weatherReports.update({
|
|
|
91
91
|
data: { status: 'ready' },
|
|
92
92
|
readAt: snap.stamp,
|
|
93
93
|
onStale: 'reject',
|
|
94
|
-
wait: 'confirmed',
|
|
95
94
|
});
|
|
96
95
|
```
|
|
97
96
|
|
|
97
|
+
Reactive local state changes optimistically at call time; awaiting the model
|
|
98
|
+
write waits for authoritative confirmation.
|
|
99
|
+
|
|
98
100
|
Protected write options:
|
|
99
101
|
|
|
100
102
|
| Option | Purpose |
|
|
101
103
|
|---|---|
|
|
102
104
|
| `readAt` | The state cursor the write was based on. |
|
|
103
105
|
| `onStale` | Stale-state policy. Prefer `reject` for agent writes. |
|
|
104
|
-
| `wait` | `queued` resolves after local queueing; `confirmed` waits for server acceptance. |
|
|
105
106
|
| `idempotencyKey` | Stable key for retry-safe writes. The SDK generates one when omitted. |
|
|
106
107
|
| `timeout` | Maximum time to wait for the write call. |
|
|
107
108
|
|
|
@@ -113,7 +114,7 @@ row, `claim` waits for them, re-reads the fresh row, then hands it to you — so
|
|
|
113
114
|
two writers serialize instead of clobbering. A claim is temporary: it expires
|
|
114
115
|
on its own if the holder stops, and is never saved as a row.
|
|
115
116
|
|
|
116
|
-
You coordinate a row with calls on its model, beside `create`/`update`/`
|
|
117
|
+
You coordinate a row with calls on its model, beside `create`/`update`/`get`:
|
|
117
118
|
`ablo.<model>.claim({ id })` takes the claim and returns a handle,
|
|
118
119
|
`ablo.<model>.claim.state({ id })` reads who currently holds it (synchronous, never
|
|
119
120
|
blocks), and `ablo.<model>.claim.release({ id })` releases it early. The full
|
package/docs/cli.md
CHANGED
|
@@ -122,7 +122,7 @@ bypasses profiles for project/branch administration; the runtime key remains
|
|
|
122
122
|
| `ablo init` | Scaffold `ablo/` (`schema.ts`, client, optional Data Source / agent / component), write `.env`, install the SDK. Offers to log in at the end. |: |
|
|
123
123
|
| `ablo login` / `logout` / `whoami` / `status` | Authentication, exact credential identity, and readiness (above). | `whoami --key-env <NAME>`, `whoami --json`, `status --json` |
|
|
124
124
|
| `ablo projects list\|create\|use\|rename` | Manage projects and the active one (see [Projects](#projects)). Each project's keys/schema/data are isolated. | `--name "<display>"` (create/rename) |
|
|
125
|
-
| `ablo dev` |
|
|
125
|
+
| `ablo dev` | Ensure an isolated Git branch, wire its temporary key, push, then watch `ablo/schema.ts`. `--local` also serves local Postgres over an outbound signed connector. | `--branch <slug>`, `--branch-ttl-hours <1-168>`, `--local`, `--source <path>`, `--no-watch`, `--schema`, `--export`, `--url` |
|
|
126
126
|
| `ablo branch list\|status\|check\|create\|ensure\|credential\|delete` | Manage and diagnose immutable branch planes and expiring credentials. | Run `ablo branch --help`; use `--json` for automation. |
|
|
127
127
|
| `ablo logs` | Tail the resolved runtime credential's branch activity. Follows by default. | `-n, --tail <N>`, `--since <dur\|ts>`, `--model`, `--op`, `--json`, `--no-follow` |
|
|
128
128
|
| `ablo push` | **Hosted**: upload the schema to Ablo; the server diffs, migrates, and activates it. | `--force`, `--rename old:new`, `--backfill model.field=value`, `--schema`, `--export`, `--url` |
|
|
@@ -145,9 +145,9 @@ npx ablo docs --json # the page list, machine-readable
|
|
|
145
145
|
|
|
146
146
|
These pages ship inside the npm package, so they describe the code beside them
|
|
147
147
|
and stay reachable with no network — isolated agent environments and CI runners
|
|
148
|
-
often have none. That matters most when a project is pinned: `
|
|
149
|
-
|
|
150
|
-
the newest release, so an agent on an earlier version reads the new
|
|
148
|
+
often have none. That matters most when a project is pinned: `claim` took a
|
|
149
|
+
callback before it returned a disposable handle, and a website always describes
|
|
150
|
+
the newest release, so an agent on an earlier version reads the new shape and
|
|
151
151
|
writes a call its own package doesn't have.
|
|
152
152
|
|
|
153
153
|
Pass a slug (`coordination`), a path (`docs/coordination.md`), or a file name
|
|
@@ -166,8 +166,16 @@ npx ablo dev # discover from Git, push + watch
|
|
|
166
166
|
npx ablo dev --branch preview-pr-482 # explicit branch
|
|
167
167
|
npx ablo dev --no-watch # prepare, push once, exit
|
|
168
168
|
npx ablo dev --branch-ttl-hours 24 # change temporary-key lifetime
|
|
169
|
+
npx ablo dev --local # keep Postgres private on localhost
|
|
169
170
|
```
|
|
170
171
|
|
|
172
|
+
`--local` loads `ablo/data-source.ts` (override with `--source <path>`), registers
|
|
173
|
+
the branch as connector-only, and opens an outbound authenticated WebSocket to
|
|
174
|
+
Ablo. Commit, load, list, and outbox-event requests run through the same signed
|
|
175
|
+
Data Source handler as production; no database credential leaves your process
|
|
176
|
+
and no public tunnel is opened. Because the connector is long-lived, `--local`
|
|
177
|
+
cannot be combined with `--no-watch`.
|
|
178
|
+
|
|
171
179
|
It does not start your app, run migrations, create a database-provider branch,
|
|
172
180
|
or copy production rows. Read [Branch-first development](./branch-development.md)
|
|
173
181
|
for the exact discovery order, CI flow, database boundary, and troubleshooting.
|
|
@@ -330,5 +338,5 @@ migration can't leave clients gated against tables that don't match.
|
|
|
330
338
|
| ------------------------------------- | ------------------------------------------------------------------------ | -------------------------- |
|
|
331
339
|
| `ABLO_API_KEY` | Authenticate without `ablo login` (CI). Always overrides the stored key. |: |
|
|
332
340
|
| `ABLO_API_URL` | Control-plane / API host (`push`, `dev`, `status`). | `https://api.abloatai.com` |
|
|
333
|
-
| `ABLO_AUTH_URL` | Dashboard origin for `ablo login`'s device flow. | `https://abloatai.com`
|
|
341
|
+
| `ABLO_AUTH_URL` | Dashboard origin for `ablo login`'s device flow. | `https://www.abloatai.com` |
|
|
334
342
|
| `ABLO_CONFIG_DIR` / `XDG_CONFIG_HOME` | Where the credential file lives. | `~/.config/ablo` |
|
package/docs/client-behavior.md
CHANGED
|
@@ -57,11 +57,15 @@ const report = await ablo.weatherReports.get({ id: 'report_stockholm' });
|
|
|
57
57
|
const local = ablo.weatherReports.local.get('report_stockholm');
|
|
58
58
|
|
|
59
59
|
await ablo.weatherReports.create({ data: { location: 'Stockholm', status: 'pending' } });
|
|
60
|
-
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' }
|
|
61
|
-
await ablo.weatherReports.delete({ id: 'report_stockholm'
|
|
60
|
+
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' } });
|
|
61
|
+
await ablo.weatherReports.delete({ id: 'report_stockholm' });
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
On the reactive client, each model write changes local state optimistically
|
|
65
|
+
before the call returns. Its promise always waits for authoritative
|
|
66
|
+
confirmation, so `await update(...)` is the confirmation barrier.
|
|
67
|
+
|
|
68
|
+
Call `get`/`list` first — they fetch from the server and you `await` them.
|
|
65
69
|
After that, `local.get`/`local.list`/`local.count` read the already-synced data instantly with
|
|
66
70
|
no `await`, and stay reactive in render. Use the async pair to load, the sync trio
|
|
67
71
|
to read.
|
|
@@ -87,7 +91,6 @@ await ablo.weatherReports.update({
|
|
|
87
91
|
data: patch,
|
|
88
92
|
readAt: snap.stamp,
|
|
89
93
|
onStale: 'reject',
|
|
90
|
-
wait: 'confirmed',
|
|
91
94
|
});
|
|
92
95
|
```
|
|
93
96
|
|
|
@@ -109,7 +112,6 @@ it for reads, but it bypasses claims and ordering.
|
|
|
109
112
|
await ablo.weatherReports.update({
|
|
110
113
|
id: 'report_stockholm',
|
|
111
114
|
data: { status: 'ready' },
|
|
112
|
-
wait: 'confirmed',
|
|
113
115
|
readAt: snap.stamp,
|
|
114
116
|
onStale: 'reject',
|
|
115
117
|
idempotencyKey: 'report_stockholm:mark-ready:v1',
|
|
@@ -118,7 +120,6 @@ await ablo.weatherReports.update({
|
|
|
118
120
|
|
|
119
121
|
| Option | Purpose |
|
|
120
122
|
|---|---|
|
|
121
|
-
| `wait` | `queued` resolves after local queueing; `confirmed` waits for server acceptance. |
|
|
122
123
|
| `readAt` | State cursor the write was based on. |
|
|
123
124
|
| `onStale` | Policy when the target changed after `readAt`. Prefer `reject`. |
|
|
124
125
|
| `idempotencyKey` | Stable key for retry-safe writes. The SDK generates one when omitted. |
|
|
@@ -180,7 +181,7 @@ All SDK errors extend `AbloError` and carry a stable `type`.
|
|
|
180
181
|
import { AbloClaimedError } from '@abloatai/ablo';
|
|
181
182
|
|
|
182
183
|
try {
|
|
183
|
-
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' }
|
|
184
|
+
await ablo.weatherReports.update({ id: 'report_stockholm', data: { status: 'ready' } });
|
|
184
185
|
} catch (error) {
|
|
185
186
|
if (error instanceof AbloClaimedError) {
|
|
186
187
|
return { status: 'claimed' };
|
package/docs/coordination.md
CHANGED
|
@@ -313,7 +313,7 @@ four axes. `claim({ id })` alone is a complete call; each axis is opt-in.
|
|
|
313
313
|
|
|
314
314
|
| name | type | required | description |
|
|
315
315
|
|---|---|---|---|
|
|
316
|
-
| `id` | `string` | yes | The row id: same id as `
|
|
316
|
+
| `id` | `string` | yes | The row id: same id as `get` / `update`. |
|
|
317
317
|
| `options.fields` | field selector | no | Claim fields declared by the model's Zod schema instead of the whole row: `fields: (task) => task.status`, or `fields: (task) => [task.status, task.title]` for several. The model supplies its own fields, so autocomplete is exact, a typo does not compile, and a schema rename is a compile error at every use. Two sets conflict where they intersect, so holders of disjoint fields do not wait for each other; see [claiming part of a row](#claiming-part-of-a-row). |
|
|
318
318
|
|
|
319
319
|
*What others see* — the presence half:
|
|
@@ -510,7 +510,7 @@ summary into the same snapshot immediately.
|
|
|
510
510
|
|
|
511
511
|
**You don't subscribe to anything first.** Reading or claiming a row
|
|
512
512
|
automatically enrolls you in that row's sync group: reading it (including
|
|
513
|
-
`
|
|
513
|
+
`get`, or `claim.state` itself) gives you **read-interest**, and
|
|
514
514
|
`claim`-ing it gives you a **pinned write-intent**. So `claim.state({ id })`
|
|
515
515
|
observes co-participants on that row from **any** client — a browser, a Server
|
|
516
516
|
Action, or a Node agent — and a holder sees its own claim, with no manual
|