@ax-hub/sdk 1.0.2 → 2.1.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/README.md CHANGED
@@ -18,6 +18,48 @@ agent-first Node.js SDK for AX Hub. Designed for Claude, Codex, and other coding
18
18
  | use admin/governance APIs | [`@ax-hub/admin-sdk`](../admin-sdk/README.md) |
19
19
  | upgrade from 0.x | [Migration & Upgrade](#migration--upgrade) |
20
20
 
21
+ ## Agent field guide from live QA (2026-06-08)
22
+
23
+ Use this section when an autonomous agent only has the README and must ship against AX Hub safely.
24
+
25
+ ### 1. Runtime inputs
26
+
27
+ ```bash
28
+ export AX_HUB_PAT="<short-lived PAT>"
29
+ export AX_HUB_TENANT_ID="cc1e58f1-8e46-4ac7-96c1-190c4cdd5b70" # test tenant
30
+ export AX_HUB_TENANT_SLUG="test"
31
+ ```
32
+
33
+ - PAT auth is `tokenType: 'pat'` and is sent as `X-Api-Key`.
34
+ - JWT auth is `tokenType: 'jwt'` and is sent as `Authorization: Bearer`.
35
+ - Never log the token. Redact env dumps before saving QA artifacts.
36
+
37
+ ### 2. Fastest safe live loop
38
+
39
+ 1. Create a timestamp-suffixed private app in tenant `test`.
40
+ 2. Set an env var, list env vars, then delete it and assert it is absent.
41
+ 3. Create a table with `owner_id`, `title`, `status`, and optional metadata columns.
42
+ 4. Add a column, inspect the table, then delete the column and inspect again.
43
+ 5. Add a table grant, list grants, revoke the grant, then assert the same grant id has `revokedAt`.
44
+ 6. Insert a row, get it by id, patch it, list it with a filter, count it, browse admin rows, then delete it.
45
+ 7. Assert row get after delete returns `404` or `410`.
46
+ 8. Delete the table and assert inspect after delete returns `404` or `410`.
47
+ 9. Soft-delete the app, then permanent-delete the app.
48
+
49
+ ### 3. Deletion semantics that prevent false positives
50
+
51
+ - Row delete: prove it by a follow-up `get` returning `404` or `410`.
52
+ - Table delete: prove it by a follow-up `inspect` returning `404` or `410`.
53
+ - Table grant delete: AX Hub currently soft-revokes. `listGrants` can still return the grant, but `revokedAt` must be set. Do not assert hard disappearance.
54
+ - Deploy without git/bootstrap source can return a precondition-style 4xx. That proves error handling, not a deployment failure.
55
+
56
+ ### 4. Production evidence already collected
57
+
58
+ - Node production mutation suite exercised app/env/comments/likes/table/columns/grants/data/OAuth/publication/deploy/git preconditions with exit `0`.
59
+ - A real app bootstrap/deploy wait succeeded in production: app `d31958ad-4a9b-4dcc-8951-64a1f3060c3d`, deployment `d3a48ce3-0f9c-4bab-aa07-863c31c44460`, final status `succeeded`, followed by app permanent delete.
60
+ - Go, Java, Kotlin, Python, and Ruby each hit 189 generated backend operation facades against the same production `test` tenant with SDK exceptions `0` and backend 5xx `0`.
61
+ - Go, Java, Kotlin, Python, and Ruby each passed the strict destructive DB loop above: 22 live steps, 17 assertions, 7 cleanup calls.
62
+
21
63
  ## Magic Moment
22
64
 
23
65
  ```ts
@@ -104,8 +146,8 @@ Prefer scoped clients in examples:
104
146
 
105
147
  ```ts
106
148
  const acme = sdk.tenant('acme')
107
- await acme.apps.create({ slug: 'crm', name: 'CRM' })
108
- await acme.app('crm').tables.inspect('orders')
149
+ const app = await acme.apps.create({ slug: 'crm', name: 'CRM' })
150
+ await sdk.apps.tables.inspect(app.id, 'orders')
109
151
  await acme.app('crm').data.table('orders').list()
110
152
  ```
111
153
 
@@ -207,24 +249,25 @@ const minimal = await ordersProjection.list({ select: ['id', 'total'] as const }
207
249
  minimal.items[0].total // number
208
250
  ```
209
251
 
210
- ### Bidirectional cursor pagination
252
+ ### Offset pagination
211
253
 
212
- `list({ after, before, orderBy, limit })` supports v2 keyset cursor tokens, first-row cursors for "previous page", and order fingerprint validation. Numeric `cursor` remains as a legacy page alias for Phase-D callers.
254
+ Backend-main data routes are offset/page based. Use `pageSize` with the numeric string `cursor` returned by the previous page. `after`, `before`, `direction`, and `v1:`/`v2:` keyset tokens are rejected with `LegacyCursorError` because the backend does not support keyset cursors.
213
255
 
214
- Cursor format: SDK-generated tokens use `v2:<base64-url>` and carry an order-fingerprint + table-scope binding. The SDK rejects `v1:` tokens with `LegacyCursorError`, malformed/oversized (> 4096 chars) tokens with `InvalidCursorError`, and a cursor minted on table A replayed against table B with `InvalidCursorError` (`code: cursor_context_mismatch`). When the SDK applies a `where` clause client-side, `PaginatedList.totalIsExact` is `false` and `total` reflects the backend's unfiltered total — iterate or wait for backend filter pushdown before relying on `total`.
256
+ `total` is optional: use `items` and `nextCursor` for iteration, or call `count()` when you need a backend count for the same pushable `where` filter.
215
257
 
216
258
  ```ts
217
259
  const OrdersCursor = defineSchema({ table: 'orders', columns: { id: 'uuid' } })
218
260
  const ordersCursor = sdk.tenant('acme').app('crm').data.table(OrdersCursor)
219
- const first = await ordersCursor.list({ limit: 50, orderBy: [{ field: 'id', dir: 'asc' }] })
220
- const next = await ordersCursor.list({ after: first.nextCursor!, orderBy: [{ field: 'id', dir: 'asc' }] })
221
- const prev = await ordersCursor.list({ before: next.firstCursor!, orderBy: [{ field: 'id', dir: 'asc' }] })
222
- void prev
261
+ const first = await ordersCursor.list({ pageSize: 50 })
262
+ const next = first.nextCursor
263
+ ? await ordersCursor.list({ cursor: first.nextCursor, pageSize: 50 })
264
+ : null
265
+ void next
223
266
  ```
224
267
 
225
268
  ### Mock mode
226
269
 
227
- Use mock mode for backend-free unit tests, examples, and agent CI. Fixtures are isolated per client and still exercise where/projection/cursor/Zod behavior. Keep fixture sets small enough for in-memory tests (recommended ≤10K rows per process); use the real backend for load/perf scenarios.
270
+ Use mock mode for backend-free unit tests, examples, and agent CI. Fixtures are isolated per client and still exercise where/projection/offset pagination/Zod behavior. Keep fixture sets small enough for in-memory tests (recommended ≤10K rows per process); use the real backend for load/perf scenarios.
228
271
 
229
272
  ```ts
230
273
  const sdk = new AxHubClient({
@@ -372,7 +415,7 @@ Target was < 10ms p99 — 100x+ headroom in every path.
372
415
 
373
416
  ## Backend dependency
374
417
 
375
- Pinned against backend `main` (177 routes, 42 error codes). Re-generate types via `npm run generate` + `npm run extract-codes` after backend swagger updates.
418
+ Pinned against backend `main` (189 routes, 43 error codes). Re-generate types via `npm run generate` + `npm run extract-codes` after backend swagger updates.
376
419
 
377
420
  ## License
378
421