@abloatai/ablo 0.30.2 → 0.31.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 +26 -12
- package/README.md +12 -14
- package/dist/cli.cjs +277 -214
- package/docs/data-sources.md +133 -96
- package/docs/integration-guide.md +6 -5
- package/docs/quickstart.md +37 -30
- package/docs/schema-contract.md +7 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,41 +1,55 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.31.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
Ablo's relationship with your database is unusual, and this release is about making the setup match it. You write through Ablo — `ablo.<model>.create`, `.update`, `.delete` — and Ablo lands each change in your own Postgres through a scoped role, then reads it back off your write-ahead log to confirm it landed. Your rows never leave your database; Ablo keeps only the ordered log that lets everyone writing to that database take turns. So "connecting a database" really means handing Ablo two narrow keys: one role that can stream and read, and one that can write rows and nothing else.
|
|
8
|
+
|
|
9
|
+
Handing over those keys used to be a chore — paste some SQL, wire two connection strings into your environment, then run a second command to register them. This release folds it into one step. Point `ablo connect --apply` at an admin connection — pass it with `--url`, or let it use the `DATABASE_URL` you already have — and it runs the whole ceremony: creates the two scoped roles, publishes your tables, turns on logical decoding where the provider allows, and registers both roles with Ablo. The admin credential is used once and discarded. Nothing is written to your `.env`; Ablo generates and holds the scoped passwords itself, and your app keeps only its `ABLO_API_KEY`. When a password needs replacing, `ablo connect --rotate` mints a new pair and re-registers them in the same step.
|
|
10
|
+
|
|
11
|
+
The shape is deliberate, and it follows one rule: a credential can only have one owner. The admin connection is yours — it's the key to your whole database, so Ablo uses it once and never stores it. The two scoped roles are Ablo's, which is why Ablo, not you, generates their passwords: a credential Ablo owns is one it can rotate, revoke, and audit, and one you can read as plain SQL before you grant it. The same rule is why the scoped strings moved out of the generic `DATABASE_URL` into names of their own — `ABLO_REPLICATION_DATABASE_URL` for the reader, `ABLO_WRITE_DATABASE_URL` for the writer. Nearly every app already sets `DATABASE_URL` to reach its own database, so a replication-only credential left there breaks the app's writes, and a `--check` that quietly falls through to it can report success against the wrong database entirely. `--check` still reads the old name, with a warning, through 0.31.x, and stops in 0.32.0; `--register` refuses it today. The one job `DATABASE_URL` keeps is the honest one: the one-time admin input to `--apply`.
|
|
12
|
+
|
|
13
|
+
Where this is heading: connecting should eventually be a single sentence to an agent — "connect my Postgres" — with the CLI recognizing your provider, doing everything your credentials allow, and asking once for the single privileged step it can't do for you (a reboot to enable logical replication, an OAuth grant). `--rotate` is the first piece of the credential lifecycle that vision needs, and the `ABLO_`-prefixed names clear away the last fossils of the old dial-in era. The direction is steady: fewer keys, held more briefly, with the one irreversible act always surfaced for a human to approve.
|
|
14
|
+
|
|
3
15
|
## 0.30.2
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
6
18
|
|
|
7
|
-
- **`ablo connect --apply` sets up your database for you
|
|
19
|
+
- **`ablo connect --apply` sets up your database for you.** Connecting a database used to mean pasting a block of SQL into a psql prompt. Now one command does it. `--apply` creates the two scoped roles, publishes your tables, turns on logical replication where your provider allows, and points `DATABASE_URL` and `ABLO_WRITE_DATABASE_URL` at the new least-privilege roles. Then it checks its own work, reconnecting as the replication role to confirm the database is ready.
|
|
20
|
+
|
|
21
|
+
Nothing leaves your machine. It runs against the admin credential already in `DATABASE_URL`, asks before it changes anything, and writes the passwords it generates to your environment file rather than printing them. Every step is safe to run twice. When a managed provider needs a restart to finish — RDS, Neon — it does everything else and hands you that one step, with the exact fix. Prefer to run the SQL yourself? `ablo connect` still prints it, and `--show-sql` shows it inline first.
|
|
8
22
|
|
|
9
23
|
## 0.30.1
|
|
10
24
|
|
|
11
25
|
### Patch Changes
|
|
12
26
|
|
|
13
|
-
- **Writes land in your own database
|
|
27
|
+
- **Writes land in your own database.** Connect a database with `ablo connect` and the models you already read become writable. `ablo.<model>.create`, `.update`, and `.delete` route through Ablo's commit chokepoint and into your database — no connection string in the client. A claim you hold is checked at the chokepoint before the write applies, so agents and people take turns on a row whether they reach it by reading from Ablo or by writing back to your own database.
|
|
14
28
|
|
|
15
|
-
A write
|
|
29
|
+
A write returns as soon as it is durably accepted; the receipt calls that `queued`. For the authoritative outcome, pass `wait: 'confirmed'`. Ablo watches the change actually land in your database — the write-ahead-log echo for a replicated source, the event feed for a signed endpoint — and promotes the receipt to `confirmed` before the call returns. The code is the same either way; only the feed Ablo waits on differs.
|
|
16
30
|
|
|
17
|
-
- **A
|
|
31
|
+
- **A clearer error when a table isn't ready yet.** `ablo push` records your models, but a plane's physical tables are created separately, out of band — so a model can exist in the schema before its table does. Read or write one in that window and you now get a typed `model_not_provisioned` error (HTTP 409) that says what's missing and how to fix it, instead of an opaque failure.
|
|
18
32
|
|
|
19
33
|
## 0.30.0
|
|
20
34
|
|
|
21
35
|
### Minor Changes
|
|
22
36
|
|
|
23
|
-
- **
|
|
37
|
+
- **The `databaseUrl` client option is gone.** Your database connects to Ablo out of band, not through the client. Ablo is Stripe-shaped: the client only ever talks to the engine. You build it with `Ablo({ schema, apiKey })` — add `transport: 'http'` for a stateless worker — and it reaches Ablo over WebSocket or HTTP. It never opens a Postgres connection of its own. How your data connects is a separate, one-time step, and it takes one of two shapes:
|
|
24
38
|
|
|
25
|
-
- **Logical replication
|
|
26
|
-
- **
|
|
39
|
+
- **Logical replication, the default.** `ablo connect` registers your database, and Ablo tails its write-ahead log. Your rows stay where they are; Ablo reads the change stream and keeps its own log.
|
|
40
|
+
- **A signed endpoint, the fallback.** When a database can't grant a `REPLICATION` role, your app exposes a signed HTTP endpoint for Ablo to call. Same principle — the credentials stay with your app.
|
|
27
41
|
|
|
28
|
-
|
|
42
|
+
With no connection string in the client, `ablo init` no longer scaffolds the `--storage direct` connector, the last thing that wrote `databaseUrl: process.env.DATABASE_URL` into generated code. Run `ablo init --storage replication` (the default) or `--storage endpoint`.
|
|
29
43
|
|
|
30
|
-
- **
|
|
44
|
+
- **A claim's work label is `description`, not `reason`.** A claim is an advisory lock: you take it on a row before editing, and other participants — agents or people — see it's held and wait. The free-text note that says what you're doing is now `description`, the line a peer reads when it lands on a row you hold: `ablo.<model>.claim({ id, description: 'Renaming the task to match the brief' })`. The old `reason` option is gone, with no alias, so a call still passing it won't type-check — rename it. (This is a different field from the `reason` on a rejected or lost claim — `'conflict'`, `'expired'`, `'preempted'` — which explains why coordination said no, and hasn't changed.)
|
|
31
45
|
|
|
32
|
-
- **Claim a key Ablo hasn't synced: `ablo.<model>.claim(id)`.** Claims now come in two shapes
|
|
46
|
+
- **Claim a key Ablo hasn't synced: `ablo.<model>.claim(id)`.** Claims now come in two shapes, matched to whether Ablo holds the row. The object form, `claim({ id })`, loads the row from Ablo's synced pool and returns a `HeldClaim<T>` with its `.data`. The new string form, `claim(id)`, locks a row that lives only in your database — one Ablo has never synced — skipping the pool load and the `entity_not_found` throw, and returns a `HeldLease`: a `HeldClaim` without the data. This is Rung 0 of the connect ladder: coordination with no replication grant, no schema change, no `organization_id` column — so you can coordinate agents against your database before wiring any sync at all. `await using` releases either shape at end of scope, and the object form is untouched, so nothing you've already written changes.
|
|
33
47
|
|
|
34
48
|
### Patch Changes
|
|
35
49
|
|
|
36
|
-
- **`ablo push` defers provisioning instead of failing when
|
|
50
|
+
- **`ablo push` defers provisioning instead of failing when a role can't run DDL.** When the engine role bound to a plane can't create or alter tables, `push` no longer aborts. It records the schema and defers the table changes for a role that can run them — so your models still register, rather than the whole command stalling on a privilege the plane may never grant the engine.
|
|
37
51
|
|
|
38
|
-
- **Schema-drift
|
|
52
|
+
- **Schema-drift stops false-alarming on `selectModels` / `omitModels` clients.** A projected client carries a deliberate subset of the schema, so its hash differs from the full one by design. The check now compares against the projection's own source hash — so a correctly projected app no longer sees a phantom drift warning at startup.
|
|
39
53
|
|
|
40
54
|
## 0.29.3
|
|
41
55
|
|
package/README.md
CHANGED
|
@@ -430,12 +430,11 @@ each other's changes in real time — that's the default, not a feature you turn
|
|
|
430
430
|
- `useAblo(...)` gives React clients the live row, kept current automatically.
|
|
431
431
|
- `ablo.<model>.claim({ id })` / `claim.state({ id })` / `claim.queue({ id })` let humans and agents coordinate (and observe) active work on a row — and the line waiting behind it — before a write lands.
|
|
432
432
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
— but they are not a requirement for your normal writes to show up.
|
|
433
|
+
Writes go through Ablo. `ablo.<model>.create/update/delete` and the HTTP write
|
|
434
|
+
endpoint enter Ablo's commit chokepoint — where claims, ordering, and idempotency
|
|
435
|
+
are enforced — and Ablo lands the change in your database. It then tails the WAL to
|
|
436
|
+
confirm the row landed and fans the confirmed change out to every connected client.
|
|
437
|
+
One surface for humans, servers, and agents; one place coordination happens.
|
|
439
438
|
|
|
440
439
|
## HTTP Writes
|
|
441
440
|
|
|
@@ -458,19 +457,18 @@ curl https://api.abloatai.com/api/v1/commits \
|
|
|
458
457
|
|
|
459
458
|
## Your Database
|
|
460
459
|
|
|
461
|
-
In production, every schema model is backed by **your own database
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
460
|
+
In production, every schema model is backed by **your own database**, and that's
|
|
461
|
+
where your rows live. You write through Ablo; it lands each change in your Postgres
|
|
462
|
+
through a scoped role, then tails the WAL to confirm it and fan it out. Ablo holds
|
|
463
|
+
the ordered transaction log and coordination — never your rows. Two ways it
|
|
465
464
|
connects:
|
|
466
465
|
|
|
467
466
|
| | How Ablo connects to your Postgres | Use when |
|
|
468
467
|
| --- | --- | --- |
|
|
469
|
-
|
|
|
470
|
-
| **Signed endpoint** (fallback) | Your app exposes one route built from an ORM adapter (`prismaDataSource` / `drizzleDataSource`); Ablo
|
|
468
|
+
| **`ablo connect`** (primary) | Sets up logical replication and a scoped writer role (`npx ablo connect --apply` does it end to end). Ablo writes your rows through the writer role and reads them back over the WAL to confirm — it writes rows but runs no DDL and owns no schema. | Your database can grant a `REPLICATION` role (most can). |
|
|
469
|
+
| **Signed endpoint** (fallback) | Your app exposes one route built from an ORM adapter (`prismaDataSource` / `drizzleDataSource`); Ablo writes and confirms through it. Needs no replication setup. | Your database **can't** grant a replication role (a locked-down managed DB). |
|
|
471
470
|
|
|
472
|
-
Your database is the system of record
|
|
473
|
-
coordination, never your rows. See
|
|
471
|
+
Your database is the system of record. See
|
|
474
472
|
[Connect Your Database](./docs/data-sources.md).
|
|
475
473
|
|
|
476
474
|
## Configuration
|