@abloatai/ablo 0.9.1 → 0.9.3

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.
Files changed (79) hide show
  1. package/AGENTS.md +84 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +53 -27
  4. package/dist/BaseSyncedStore.d.ts +2 -36
  5. package/dist/BaseSyncedStore.js +11 -55
  6. package/dist/NetworkMonitor.js +4 -1
  7. package/dist/SyncClient.d.ts +22 -5
  8. package/dist/SyncClient.js +77 -0
  9. package/dist/SyncEngineContext.js +5 -1
  10. package/dist/agent/index.js +1 -1
  11. package/dist/api/index.d.ts +1 -1
  12. package/dist/auth/index.js +3 -1
  13. package/dist/cli.cjs +302645 -0
  14. package/dist/client/Ablo.d.ts +19 -52
  15. package/dist/client/Ablo.js +30 -106
  16. package/dist/client/ApiClient.d.ts +1 -113
  17. package/dist/client/ApiClient.js +39 -238
  18. package/dist/client/auth.js +32 -2
  19. package/dist/client/createInternalComponents.js +1 -1
  20. package/dist/client/createModelProxy.d.ts +9 -0
  21. package/dist/client/createModelProxy.js +34 -10
  22. package/dist/client/httpClient.d.ts +5 -6
  23. package/dist/client/httpClient.js +2 -3
  24. package/dist/client/index.d.ts +1 -1
  25. package/dist/client/persistence.d.ts +6 -1
  26. package/dist/client/persistence.js +1 -1
  27. package/dist/client/registerDataSource.d.ts +4 -4
  28. package/dist/client/registerDataSource.js +39 -31
  29. package/dist/client/writeOptionsSchema.d.ts +50 -0
  30. package/dist/client/writeOptionsSchema.js +57 -0
  31. package/dist/core/index.d.ts +18 -26
  32. package/dist/core/index.js +22 -46
  33. package/dist/errorCodes.d.ts +13 -0
  34. package/dist/errorCodes.js +19 -4
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.js +8 -1
  37. package/dist/interfaces/index.d.ts +14 -4
  38. package/dist/mutators/UndoManager.d.ts +48 -5
  39. package/dist/mutators/UndoManager.js +166 -1
  40. package/dist/react/AbloProvider.d.ts +18 -8
  41. package/dist/react/index.d.ts +1 -1
  42. package/dist/react/index.js +1 -1
  43. package/dist/react/useUndoScope.js +7 -0
  44. package/dist/schema/ddl.js +2 -1
  45. package/dist/schema/field.js +2 -1
  46. package/dist/schema/serialize.js +2 -1
  47. package/dist/server/commit.d.ts +4 -5
  48. package/dist/server/storage-mode.d.ts +7 -0
  49. package/dist/server/storage-mode.js +6 -0
  50. package/dist/source/adapters/drizzle.js +3 -2
  51. package/dist/source/adapters/kysely.d.ts +68 -0
  52. package/dist/source/adapters/kysely.js +210 -0
  53. package/dist/source/adapters/memory.js +2 -1
  54. package/dist/source/adapters/prisma.js +3 -2
  55. package/dist/source/index.js +2 -1
  56. package/dist/transactions/TransactionQueue.d.ts +6 -7
  57. package/dist/transactions/TransactionQueue.js +33 -9
  58. package/dist/types/streams.d.ts +2 -1
  59. package/dist/utils/duration.js +3 -2
  60. package/dist/wire/frames.d.ts +6 -8
  61. package/docs/api.md +1 -1
  62. package/docs/cli.md +17 -4
  63. package/docs/client-behavior.md +1 -1
  64. package/docs/data-sources.md +129 -125
  65. package/docs/examples/ai-sdk-tool.md +11 -5
  66. package/docs/examples/existing-python-backend.md +26 -4
  67. package/docs/examples/nextjs.md +3 -2
  68. package/docs/examples/scoped-agent.md +38 -11
  69. package/docs/guarantees.md +2 -2
  70. package/docs/identity.md +86 -59
  71. package/docs/index.md +2 -2
  72. package/docs/integration-guide.md +89 -61
  73. package/docs/mcp.md +1 -1
  74. package/docs/quickstart.md +84 -37
  75. package/docs/react.md +39 -28
  76. package/docs/schema-contract.md +2 -4
  77. package/llms-full.txt +360 -0
  78. package/llms.txt +30 -18
  79. package/package.json +23 -3
package/llms.txt CHANGED
@@ -6,6 +6,14 @@ Here is the problem it solves. Two writers touch `report_stockholm` at once. The
6
6
 
7
7
  Use AI SDK for the agent loop. Use Ablo when agent reads and writes must persist, coordinate with concurrent work, and leave an audit trail.
8
8
 
9
+ ## Start here
10
+
11
+ First action when integrating into an app: run `npx ablo init --yes --framework <nextjs|vite|remix|vanilla> --storage datasource`. Agents have no TTY — `--yes` is REQUIRED or it HANGS. It scaffolds `ablo/schema.ts`, the client, the Data Source endpoint, and (for Next.js) the browser provider + session route, all on the current API. Edit the generated files rather than hand-writing from this doc.
12
+
13
+ Second: make sure a key exists. Check `ABLO_API_KEY` in the environment, then `.env.local`. If neither has one, ask the HUMAN to run `npx ablo login` once — it opens a browser and saves a `sk_test_` key locally; an agent must NOT run it. You never copy the key by hand: the next step writes it into `.env.local` for you.
14
+
15
+ Then PUSH — this is the step everything depends on. The server keeps its OWN copy of the schema. Run `npx ablo dev --no-watch`: it pushes `ablo/schema.ts` (test mode) AND writes `ABLO_API_KEY` into `.env.local` from the stored login. Until the schema is pushed, EVERY write to a new or changed model fails with `server_execute_unknown_model`. Re-run it after schema changes (`npx ablo push` also works once the key is wired; bare `npx ablo dev` watches forever — don't, you have no TTY).
16
+
9
17
  ## Use this API
10
18
 
11
19
  ```ts
@@ -23,15 +31,16 @@ const schema = defineSchema({
23
31
 
24
32
  const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY });
25
33
 
26
- const report = await ablo.weatherReports.retrieve('report_stockholm');
34
+ const report = await ablo.weatherReports.retrieve({ id: 'report_stockholm' });
27
35
  if (!report) throw new Error('Row not found');
28
36
 
29
- const updated = await ablo.weatherReports.claim('report_stockholm', async (report) => {
30
- return ablo.weatherReports.update(
31
- report.id,
32
- { status: 'ready', forecast: await getForecast(report) },
33
- { wait: 'confirmed' },
34
- );
37
+ // Claim the row (waits if someone else holds it), read the fresh copy off
38
+ // `claim.data`, write, then auto-release at the end of this scope (`await using`).
39
+ await using claim = await ablo.weatherReports.claim({ id: 'report_stockholm' });
40
+ const updated = await ablo.weatherReports.update({
41
+ id: claim.data.id,
42
+ data: { status: 'ready', forecast: await getForecast(claim.data) },
43
+ wait: 'confirmed',
35
44
  });
36
45
  ```
37
46
 
@@ -43,10 +52,10 @@ hosted schema push, and schema-version gating. Do not invent a parallel
43
52
  string-keyed write path for rows that belong to a schema model.
44
53
 
45
54
  For full integrations, use `integration-guide` as the canonical doc. It covers
46
- the same model API across Ablo-managed state, Data Source-backed app databases,
55
+ the same model API across your own Data Source-backed app databases,
47
56
  React selectors, multiplayer, and future agent workers.
48
57
 
49
- Reads come in two flavors, and you pick by whether you can wait. `retrieve(id)`
58
+ Reads come in two flavors, and you pick by whether you can wait. `retrieve({ id })`
50
59
  (one row) and `list({ where })` (many) are async — they hit the server and return
51
60
  a Promise, so await them. `get(id)`, `getAll({ where })`, and `getCount({ where })`
52
61
  are synchronous — they read the local graph and are reactive in render, so no
@@ -68,7 +77,7 @@ first integration path.
68
77
  Multiplayer is not a separate mode. When human UI, server actions, and agents use
69
78
  the same schema client and write through `ablo.<model>`, Ablo coordinates the
70
79
  shared model stream: confirmed deltas fan out to subscribers, active claims are
71
- visible through `claim.state(id)`, and stale writes can be rejected with `readAt`.
80
+ visible through `claim.state({ id })`, and stale writes can be rejected with `readAt`.
72
81
 
73
82
  If an app writes directly to its own database outside Ablo, that write bypasses
74
83
  coordination until the app reports it through Data Source events.
@@ -76,7 +85,7 @@ coordination until the app reports it through Data Source events.
76
85
  ## Nouns
77
86
 
78
87
  - `Model client` is the typed `ablo.<model>` object generated from schema.
79
- - `Claim` holds a model row while slow work runs; `claim.state(id)` observes it.
88
+ - `Claim` holds a model row while slow work runs; `claim.state({ id })` observes it.
80
89
  - `Commit` is the durable protocol write behind `ablo.<model>.update(...)`.
81
90
  - `Receipt` confirms the commit.
82
91
 
@@ -109,23 +118,25 @@ A schema is model fields and relations. Advanced schema helpers such as `mutable
109
118
 
110
119
  Do not add `databaseURL` to `Ablo(...)`. Application and agent code use `ABLO_API_KEY`.
111
120
 
112
- Every schema model has a backing store. By default, Ablo stores rows for declared models, so `ablo.<model>.create/update/delete` write to Ablo-managed state. If the customer database is canonical, expose a Data Source endpoint. With Prisma or Drizzle this is ONE line pass an ORM `adapter` and it owns the transaction, idempotency, and outbox (no hand-written `commit`/`events`):
121
+ Every schema model is backed by a database, and the default is YOUR OWN. Keep your rows in your Postgres and expose a Data Source endpoint that hands Ablo an ORM `adapter` (Drizzle is the default; Prisma and Kysely are also supported) it owns the transaction, exactly-once idempotency, and outbox in ONE pass (no hand-written `commit`/`events`). Your `DATABASE_URL` lives in your app, never in `Ablo(...)`; run `npx ablo migrate` to provision the synced-model tables in your DB.
113
122
 
114
123
  ```ts
115
124
  // app/api/ablo/source/route.ts
116
125
  import { dataSourceNext } from '@abloatai/ablo/source/next';
117
- import { prismaDataSource } from '@abloatai/ablo/source';
126
+ import { drizzleDataSource } from '@abloatai/ablo/source/drizzle';
118
127
  import { schema } from '@/ablo/schema';
119
- import { prisma } from '@/lib/prisma';
128
+ import { db } from '@/db';
129
+
130
+ export const runtime = 'nodejs'; // the route touches your database
120
131
 
121
132
  export const { POST } = dataSourceNext({
122
133
  schema,
123
134
  apiKey: process.env.ABLO_API_KEY!,
124
- adapter: prismaDataSource(prisma, schema), // or drizzleDataSource(db, tables)
135
+ adapter: drizzleDataSource(db, schema), // or prismaDataSource(prisma, schema) / kyselyDataSource(db, schema)
125
136
  });
126
137
  ```
127
138
 
128
- `npx ablo init` generates this file for you (see CLI below). Customer-owned app database credentials stay private — Ablo only calls the endpoint.
139
+ `npx ablo init` defaults to this it scaffolds the endpoint and the `DATABASE_URL` for you (see CLI below). Your app database credentials stay private — Ablo only calls the endpoint.
129
140
 
130
141
  ## Sandboxes
131
142
 
@@ -156,6 +167,7 @@ Import from these public paths only:
156
167
  - `@abloatai/ablo/source` — `dataSource`, the `DataSourceAdapter` spine, `prismaDataSource`. For a customer-canonical Data Source endpoint.
157
168
  - `@abloatai/ablo/source/next` — `dataSourceNext` (Next.js App Router `{ POST }`).
158
169
  - `@abloatai/ablo/source/drizzle` — `drizzleDataSource`.
170
+ - `@abloatai/ablo/source/kysely` — `kyselyDataSource`.
159
171
  - `@abloatai/ablo/source/conformance` — `runDataSourceTests` to prove a custom adapter/handler.
160
172
 
161
173
  Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, or internal subpaths. (`/source` IS public — it's the Data Source endpoint surface above.)
@@ -165,8 +177,8 @@ Do not teach `/api`, `/agent`, `/ai-sdk`, `/core`, `/realtime`, or internal subp
165
177
  `ablo init` and other prompts need a TTY; an agent/CI run has none and will HANG. Always:
166
178
 
167
179
  - `npx ablo init --yes` (flags: `--framework`, `--auth`, `--storage`, `--no-agent`, `--no-pull`, `--no-install`, `--no-login`). Generates `ablo/schema.ts` + the `ablo/data-source.ts` endpoint above.
168
- - Authenticate with the `ABLO_API_KEY` env var. Do NOT run `ablo login` (opens a browser).
180
+ - Key: see "Start here" env → `.env.local` ask the human to `npx ablo login`; never run `login` yourself, never copy keys by hand (`ablo dev` writes `.env.local`).
169
181
  - Adopt an existing DB: `npx ablo pull prisma [path]` / `npx ablo pull drizzle <module>`.
170
- - `npx ablo dev --no-watch` (default watches forever); `npx ablo logs --no-follow` (default tails forever); `npx ablo mode test|live` (always pass the arg). `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
182
+ - `npx ablo dev --no-watch` pushes the schema (test mode) AND writes `ABLO_API_KEY` to `.env.local` (default watches forever); `npx ablo logs --no-follow` (default tails forever); `npx ablo mode test|live` (always pass the arg). `npx ablo push`/`status`/`pull`/`check`/`generate` are one-shot.
171
183
 
172
184
  Canonical docs to read before integrating: `quickstart`, `schema-contract`, `integration-guide`, `guarantees`, `client-behavior`, `data-sources`, `examples/existing-python-backend`, `api`, `examples/ai-sdk-tool`, and `examples/server-agent`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "description": "State control API for AI agents and collaborative apps.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -9,6 +9,9 @@
9
9
  },
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
12
+ "bin": {
13
+ "ablo": "./dist/cli.cjs"
14
+ },
12
15
  "exports": {
13
16
  ".": {
14
17
  "types": "./dist/index.d.ts",
@@ -65,6 +68,11 @@
65
68
  "import": "./dist/source/adapters/drizzle.js",
66
69
  "default": "./dist/source/adapters/drizzle.js"
67
70
  },
71
+ "./source/kysely": {
72
+ "types": "./dist/source/adapters/kysely.d.ts",
73
+ "import": "./dist/source/adapters/kysely.js",
74
+ "default": "./dist/source/adapters/kysely.js"
75
+ },
68
76
  "./source/next": {
69
77
  "types": "./dist/source/next.d.ts",
70
78
  "import": "./dist/source/next.js",
@@ -106,6 +114,7 @@
106
114
  "examples",
107
115
  "AGENTS.md",
108
116
  "llms.txt",
117
+ "llms-full.txt",
109
118
  "LICENSE",
110
119
  "NOTICE",
111
120
  "README.md",
@@ -113,13 +122,17 @@
113
122
  ],
114
123
  "scripts": {
115
124
  "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
116
- "build": "npm run clean && tsc -p tsconfig.build.json",
125
+ "build": "npm run clean && tsc -p tsconfig.build.json && npm run build:cli",
126
+ "build:cli": "tsup --config tsup.cli.config.ts",
127
+ "typecheck:cli": "tsc -p tsconfig.cli.json",
117
128
  "pack:check": "npm_config_cache=${TMPDIR:-/tmp}/ablo-npm-cache npm pack --dry-run",
118
129
  "lint:imports": "node scripts/check-js-extensions.mjs",
119
130
  "generate:errors": "tsx scripts/generate-error-docs.mts",
120
131
  "lint:errors": "tsx scripts/check-error-docs.mts",
121
132
  "lint:pkg": "publint",
122
133
  "prepublishOnly": "npm run build && npm run lint:pkg",
134
+ "check:dist": "node scripts/check-dist-fresh.mjs",
135
+ "pretest": "node scripts/check-dist-fresh.mjs",
123
136
  "test": "jest",
124
137
  "test:unit": "jest --testPathPattern=__tests__/unit",
125
138
  "test:integration": "jest --testPathPattern=__tests__/integration",
@@ -159,7 +172,7 @@
159
172
  },
160
173
  "peerDependencies": {
161
174
  "react": "^19.0.0",
162
- "drizzle-orm": ">=0.30.0"
175
+ "drizzle-orm": ">=0.44.0"
163
176
  },
164
177
  "peerDependenciesMeta": {
165
178
  "react": {
@@ -177,6 +190,8 @@
177
190
  },
178
191
  "devDependencies": {
179
192
  "@ai-sdk/provider": "^3.0.0",
193
+ "@clack/prompts": "^0.11.0",
194
+ "@prisma/client": "^7.3.0",
180
195
  "@types/jest": "^29.5.0",
181
196
  "@types/node": "^22.0.0",
182
197
  "@types/react": "^19.0.0",
@@ -189,10 +204,15 @@
189
204
  "fast-check": "^3.0.0",
190
205
  "jest": "^29.7.0",
191
206
  "jest-environment-jsdom": "^29.7.0",
207
+ "jiti": "^2.7.0",
192
208
  "mobx": "^6.13.7",
209
+ "picocolors": "^1.1.0",
210
+ "postgres": "^3.4.0",
193
211
  "react": "^19.0.0",
194
212
  "react-dom": "^19.0.0",
195
213
  "ts-jest": "^29.4.0",
214
+ "ts-morph": "^26.0.0",
215
+ "tsup": "^8.0.0",
196
216
  "typescript": "^5.8.3",
197
217
  "publint": "^0.3.21"
198
218
  }