@abloatai/ablo 0.43.0 → 0.44.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 CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.44.0
4
+
5
+ ### A scope denial names the wall it hit
6
+
7
+ `capability_scope_denied` now distinguishes the Ablo capability allowlist
8
+ from the customer database's row-level security. The error carries the
9
+ required capability, the resolved operations, the participant and user
10
+ principal, the branch, the organization and project, and any applied session
11
+ settings, so "permission denied" is a diagnosis instead of a dead end: you
12
+ can see whether your grant was missing a verb or whether your own database's
13
+ row policy rejected the session context Ablo applied.
14
+
15
+ ### Write failures carry their request id
16
+
17
+ A WebSocket write failure now carries the `requestId` the server logged it
18
+ under, and a `wait: 'confirmed'` write rejects with the complete typed error
19
+ rather than a bare failure, so the error you catch is the error the server
20
+ recorded.
21
+
22
+ ### `doctor` reports readiness, not destiny
23
+
24
+ `doctor` now says infrastructure is ready rather than promising a write will
25
+ succeed, because database constraints and row-level security still apply at
26
+ write time. The debugging guide explains how to read the new diagnostics,
27
+ and documents that `list()` may answer from the local pool while
28
+ `list({ type: 'complete' })` waits for the server round trip.
29
+
3
30
  ## 0.43.0
4
31
 
5
32
  ### Keys are branch-first
package/docs/debugging.md CHANGED
@@ -15,12 +15,13 @@ const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, debug: true });
15
15
 
16
16
  ## CLI environment and target
17
17
 
18
- `push`, `dev`, and `status` follow the application-facing chain: exported
19
- `ABLO_API_KEY`, `.env.local`, `.env`, then the stored credential. An exported
20
- value wins over project files. Connect-family administration is intentionally
21
- stricter: it reads only the process environment and stored login, so an ambient
22
- file cannot silently choose the plane a database mutation acts on. When a file
23
- contains the missing key, the refusal names that rule and the remedy.
18
+ Read-only diagnostics (`status`, `whoami`, `logs`, and `connect locate/check`)
19
+ may inspect the application-facing chain: exported `ABLO_API_KEY`,
20
+ `.env.local`, `.env`, then the stored credential. An exported value wins over
21
+ project files. Mutations (`push` and `connect apply/rotate/register/deregister`)
22
+ are intentionally stricter: they read the process environment, an explicit
23
+ `--env-file`, or a stored compatibility credential. An ambient file cannot
24
+ silently choose the branch a mutation acts on.
24
25
 
25
26
  Use the two diagnostics according to the question:
26
27
 
@@ -232,3 +233,57 @@ AbloValidationError [model_required_field_missing]: A required field was absent.
232
233
  ```
233
234
 
234
235
  Branch on `err.code` (stable) — never on the message (rewordable). See [Client Behavior](./client-behavior.md) for the full error model and which codes are safe to retry.
236
+
237
+ ### Diagnosing `capability_scope_denied`
238
+
239
+ The same stable code covers two different enforcement layers, so inspect
240
+ `error.details.origin`:
241
+
242
+ - `capability_allowlist`: the branch/session credential did not grant the
243
+ operation. `requiredCapability.scope` names the missing `model.verb`, and
244
+ `details.resolvedOperations` shows the grants the server actually resolved.
245
+ - `database_row_level_security`: Ablo's capability gate allowed the operation,
246
+ but Postgres rejected it under the customer table's RLS policy.
247
+ `details.databaseSessionContext` shows the organization, project, branch,
248
+ participant kind, user principal, and custom session-setting values applied
249
+ to that transaction.
250
+
251
+ Every rejected live commit carries `requestId` on the thrown error and
252
+ `request_id` in its JSON form and warning line:
253
+
254
+ ```ts
255
+ import { AbloError } from '@abloatai/ablo';
256
+
257
+ try {
258
+ await ablo.documents.create({
259
+ data,
260
+ wait: 'confirmed',
261
+ });
262
+ } catch (error) {
263
+ if (error instanceof AbloError) {
264
+ console.error(error.code, error.requestId, error.requiredCapability, error.details);
265
+ }
266
+ }
267
+ ```
268
+
269
+ With `wait: 'confirmed'`, the awaited call rejects with that complete typed
270
+ error. `onMutationFailure` remains the notification channel for optimistic
271
+ writes that return before the server answers; it is not required to recover
272
+ details from a confirmed write.
273
+
274
+ ### Local reads versus a confirmed server read
275
+
276
+ `list()` without a completeness option may return the current local pool
277
+ immediately. That is why it can be empty while Postgres contains rows: it is not
278
+ evidence that the replication source has no history.
279
+
280
+ Use:
281
+
282
+ ```ts
283
+ await ablo.documents.list({ type: 'complete' });
284
+ ```
285
+
286
+ `type: 'complete'` waits for a server round trip and returns the confirmed
287
+ result. `type: 'unknown'` returns the local result immediately and refreshes it
288
+ in the background. The distinction is freshness/completeness, not claimed
289
+ versus unclaimed data.
@@ -2,7 +2,7 @@
2
2
 
3
3
  > What production takes: a database Ablo can reach, a key minted for the plane you mean, and a schema push in the deploy.
4
4
 
5
- One command answers the question this page exists for — would a write succeed
5
+ One command answers whether the infrastructure needed for a write is ready
6
6
  right now, and if not, why:
7
7
 
8
8
  ```bash
@@ -25,7 +25,7 @@ ABLO_API_KEY=sk_… npx ablo status
25
25
  • fulfilments typename=fulfilments
26
26
  • reviews typename=reviews
27
27
 
28
- ✓ ready — a write should succeed
28
+ write infrastructure is ready — database constraints and row-level policies still apply
29
29
  ```
30
30
 
31
31
  `status` asks the routing authority rather than sampling a read, because reads
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.43.0",
3
+ "version": "0.44.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",
@@ -124,8 +124,8 @@
124
124
  "directory": "packages/ablo"
125
125
  },
126
126
  "dependencies": {
127
- "@abloatai/humans": "^0.43.0",
128
- "@abloatai/transaction": "^0.43.0"
127
+ "@abloatai/humans": "^0.44.0",
128
+ "@abloatai/transaction": "^0.44.0"
129
129
  },
130
130
  "peerDependencies": {
131
131
  "ai": "^6.0.0 || ^7.0.0",