@abloatai/ablo 0.24.0 → 0.26.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.
Files changed (246) hide show
  1. package/AGENTS.md +5 -3
  2. package/CHANGELOG.md +33 -0
  3. package/README.md +45 -36
  4. package/dist/BaseSyncedStore.d.ts +72 -195
  5. package/dist/BaseSyncedStore.js +240 -625
  6. package/dist/Database.d.ts +8 -9
  7. package/dist/Database.js +9 -14
  8. package/dist/LazyReferenceCollection.js +5 -0
  9. package/dist/Model.js +6 -8
  10. package/dist/ModelRegistry.d.ts +25 -6
  11. package/dist/ModelRegistry.js +12 -6
  12. package/dist/NetworkMonitor.js +1 -1
  13. package/dist/ObjectPool.js +9 -3
  14. package/dist/SyncClient.d.ts +15 -5
  15. package/dist/SyncClient.js +152 -66
  16. package/dist/SyncEngineContext.d.ts +1 -3
  17. package/dist/SyncEngineContext.js +1 -2
  18. package/dist/agent/Agent.d.ts +21 -8
  19. package/dist/agent/Agent.js +26 -13
  20. package/dist/ai-sdk/coordination-context.js +9 -3
  21. package/dist/ai-sdk/wrap.d.ts +1 -1
  22. package/dist/ai-sdk/wrap.js +1 -1
  23. package/dist/auth/credentialPolicy.d.ts +10 -2
  24. package/dist/auth/credentialSource.d.ts +1 -13
  25. package/dist/auth/credentialSource.js +6 -13
  26. package/dist/auth/index.js +3 -3
  27. package/dist/cli.cjs +583 -287
  28. package/dist/client/Ablo.d.ts +35 -764
  29. package/dist/client/Ablo.js +64 -688
  30. package/dist/client/ApiClient.d.ts +14 -1
  31. package/dist/client/ApiClient.js +63 -9
  32. package/dist/client/auth.d.ts +8 -17
  33. package/dist/client/auth.js +42 -6
  34. package/dist/client/consoleLogger.d.ts +36 -0
  35. package/dist/client/consoleLogger.js +45 -0
  36. package/dist/client/createInternalComponents.js +1 -1
  37. package/dist/client/createModelProxy.js +10 -6
  38. package/dist/client/credentialEndpoint.d.ts +63 -0
  39. package/dist/client/credentialEndpoint.js +87 -0
  40. package/dist/client/hostedEndpoints.d.ts +24 -0
  41. package/dist/client/hostedEndpoints.js +24 -0
  42. package/dist/client/httpClient.d.ts +1 -1
  43. package/dist/client/identity.js +2 -1
  44. package/dist/client/modelRegistration.d.ts +14 -0
  45. package/dist/client/modelRegistration.js +321 -0
  46. package/dist/client/options.d.ts +396 -0
  47. package/dist/client/options.js +10 -0
  48. package/dist/client/resourceTypes.d.ts +344 -0
  49. package/dist/client/resourceTypes.js +13 -0
  50. package/dist/client/schemaConfig.d.ts +56 -0
  51. package/dist/client/schemaConfig.js +188 -0
  52. package/dist/client/sessionMint.d.ts +1 -1
  53. package/dist/client/validateAbloOptions.js +1 -1
  54. package/dist/client/wsMutationExecutor.d.ts +33 -0
  55. package/dist/client/wsMutationExecutor.js +77 -0
  56. package/dist/context.js +0 -3
  57. package/dist/coordination/index.d.ts +8 -1
  58. package/dist/coordination/index.js +24 -1
  59. package/dist/coordination/trace.d.ts +0 -1
  60. package/dist/coordination/trace.js +0 -1
  61. package/dist/core/DatabaseManager.d.ts +0 -1
  62. package/dist/core/DatabaseManager.js +26 -24
  63. package/dist/core/QueryProcessor.js +7 -8
  64. package/dist/core/QueryView.d.ts +14 -2
  65. package/dist/core/QueryView.js +4 -5
  66. package/dist/core/StoreManager.d.ts +2 -2
  67. package/dist/core/StoreManager.js +5 -1
  68. package/dist/core/index.d.ts +1 -1
  69. package/dist/core/openIDBWithTimeout.js +15 -11
  70. package/dist/core/query-utils.js +1 -1
  71. package/dist/core/storeContract.d.ts +143 -0
  72. package/dist/core/storeContract.js +16 -0
  73. package/dist/errorCodes.d.ts +11 -2
  74. package/dist/errorCodes.js +155 -137
  75. package/dist/errors.d.ts +18 -7
  76. package/dist/errors.js +14 -1
  77. package/dist/index.js +5 -0
  78. package/dist/interfaces/index.d.ts +6 -20
  79. package/dist/keys/index.js +18 -9
  80. package/dist/mutators/UndoManager.js +6 -1
  81. package/dist/mutators/defineMutators.d.ts +1 -3
  82. package/dist/policy/types.d.ts +1 -1
  83. package/dist/query/client.d.ts +11 -0
  84. package/dist/query/client.js +81 -52
  85. package/dist/react/AbloProvider.d.ts +6 -10
  86. package/dist/react/AbloProvider.js +22 -15
  87. package/dist/react/context.d.ts +2 -118
  88. package/dist/react/useAblo.js +1 -1
  89. package/dist/react/useErrorListener.js +1 -1
  90. package/dist/react/useMutationFailureListener.js +1 -1
  91. package/dist/react/useMutators.js +7 -1
  92. package/dist/react/useReactive.js +0 -1
  93. package/dist/react/useUndoScope.js +2 -2
  94. package/dist/schema/ddlLock.d.ts +39 -0
  95. package/dist/schema/ddlLock.js +51 -0
  96. package/dist/schema/field.js +1 -1
  97. package/dist/schema/index.d.ts +4 -3
  98. package/dist/schema/index.js +15 -5
  99. package/dist/schema/model.d.ts +3 -3
  100. package/dist/schema/model.js +2 -2
  101. package/dist/schema/openapi.js +5 -1
  102. package/dist/schema/queries.d.ts +1 -1
  103. package/dist/schema/queries.js +1 -0
  104. package/dist/schema/residency.d.ts +35 -0
  105. package/dist/schema/residency.js +27 -0
  106. package/dist/schema/roles.d.ts +25 -0
  107. package/dist/schema/roles.js +37 -0
  108. package/dist/schema/schema.d.ts +3 -3
  109. package/dist/schema/schema.js +11 -7
  110. package/dist/schema/serialize.d.ts +8 -8
  111. package/dist/schema/sync-delta-row.d.ts +2 -2
  112. package/dist/schema/sync-delta-row.js +2 -2
  113. package/dist/server/storage-mode.d.ts +8 -1
  114. package/dist/server/storage-mode.js +14 -4
  115. package/dist/source/adapter.d.ts +1 -1
  116. package/dist/source/adapters/drizzle.js +4 -3
  117. package/dist/source/adapters/kysely.js +4 -3
  118. package/dist/source/adapters/memory.js +1 -1
  119. package/dist/source/adapters/prisma.js +5 -4
  120. package/dist/source/conformance.js +10 -8
  121. package/dist/source/connector.js +4 -2
  122. package/dist/source/factory.d.ts +106 -0
  123. package/dist/source/factory.js +273 -0
  124. package/dist/source/index.d.ts +11 -461
  125. package/dist/source/index.js +14 -416
  126. package/dist/source/next.d.ts +1 -1
  127. package/dist/source/next.js +1 -1
  128. package/dist/source/pushQueue.d.ts +1 -1
  129. package/dist/source/pushQueue.js +6 -3
  130. package/dist/source/signing.d.ts +59 -0
  131. package/dist/source/signing.js +142 -0
  132. package/dist/source/types.d.ts +334 -0
  133. package/dist/source/types.js +44 -0
  134. package/dist/stores/ObjectStore.d.ts +1 -1
  135. package/dist/stores/ObjectStore.js +23 -23
  136. package/dist/stores/SyncActionStore.d.ts +1 -1
  137. package/dist/stores/SyncActionStore.js +70 -35
  138. package/dist/surface.d.ts +1 -1
  139. package/dist/surface.js +1 -0
  140. package/dist/sync/AreaOfInterestManager.d.ts +2 -2
  141. package/dist/sync/AreaOfInterestManager.js +1 -1
  142. package/dist/sync/BootstrapHelper.d.ts +1 -3
  143. package/dist/sync/BootstrapHelper.js +29 -8
  144. package/dist/sync/ConnectionManager.d.ts +8 -0
  145. package/dist/sync/ConnectionManager.js +19 -4
  146. package/dist/sync/HydrationCoordinator.d.ts +12 -0
  147. package/dist/sync/HydrationCoordinator.js +25 -5
  148. package/dist/sync/NetworkProbe.d.ts +3 -2
  149. package/dist/sync/NetworkProbe.js +7 -6
  150. package/dist/sync/SyncWebSocket.d.ts +71 -120
  151. package/dist/sync/SyncWebSocket.js +324 -630
  152. package/dist/sync/awaitClaimGrant.js +27 -19
  153. package/dist/sync/bootstrapApply.d.ts +60 -0
  154. package/dist/sync/bootstrapApply.js +65 -0
  155. package/dist/sync/commitFrames.d.ts +43 -0
  156. package/dist/sync/commitFrames.js +94 -0
  157. package/dist/sync/createClaimStream.js +3 -2
  158. package/dist/sync/createSnapshot.d.ts +1 -3
  159. package/dist/sync/credentialLifecycle.d.ts +175 -0
  160. package/dist/sync/credentialLifecycle.js +329 -0
  161. package/dist/sync/deltaPipeline.d.ts +111 -0
  162. package/dist/sync/deltaPipeline.js +262 -0
  163. package/dist/sync/groupChange.d.ts +111 -0
  164. package/dist/sync/groupChange.js +246 -0
  165. package/dist/sync/heartbeat.d.ts +62 -0
  166. package/dist/sync/heartbeat.js +91 -0
  167. package/dist/sync/participants.d.ts +8 -8
  168. package/dist/sync/syncCursor.d.ts +44 -0
  169. package/dist/sync/syncCursor.js +59 -0
  170. package/dist/sync/syncPlan.d.ts +62 -0
  171. package/dist/sync/syncPlan.js +51 -0
  172. package/dist/sync/wsFrameHandlers.d.ts +117 -0
  173. package/dist/sync/wsFrameHandlers.js +378 -0
  174. package/dist/testing/fixtures/bootstrap.d.ts +9 -11
  175. package/dist/testing/fixtures/deltas.d.ts +1 -1
  176. package/dist/testing/fixtures/models.js +1 -0
  177. package/dist/testing/helpers/sync-engine-harness.d.ts +0 -3
  178. package/dist/testing/helpers/sync-engine-harness.js +2 -1
  179. package/dist/testing/index.d.ts +1 -1
  180. package/dist/testing/index.js +1 -1
  181. package/dist/testing/mocks/MockSyncContext.d.ts +1 -17
  182. package/dist/testing/mocks/MockSyncContext.js +1 -32
  183. package/dist/testing/mocks/MockSyncStore.d.ts +4 -4
  184. package/dist/testing/mocks/MockWebSocket.d.ts +2 -2
  185. package/dist/transactions/TransactionQueue.d.ts +20 -56
  186. package/dist/transactions/TransactionQueue.js +101 -487
  187. package/dist/transactions/TransactionStore.d.ts +18 -0
  188. package/dist/transactions/TransactionStore.js +51 -0
  189. package/dist/transactions/coalesceRules.d.ts +34 -0
  190. package/dist/transactions/coalesceRules.js +117 -0
  191. package/dist/transactions/commitPayload.d.ts +134 -0
  192. package/dist/transactions/commitPayload.js +152 -0
  193. package/dist/transactions/deltaConfirmation.d.ts +60 -0
  194. package/dist/transactions/deltaConfirmation.js +223 -0
  195. package/dist/transactions/optimistic.d.ts +24 -0
  196. package/dist/transactions/optimistic.js +45 -0
  197. package/dist/transactions/persistedReplay.d.ts +93 -0
  198. package/dist/transactions/persistedReplay.js +105 -0
  199. package/dist/types/index.d.ts +3 -3
  200. package/dist/types/index.js +0 -5
  201. package/dist/types/modelData.d.ts +12 -0
  202. package/dist/types/modelData.js +11 -0
  203. package/dist/types/participant.d.ts +21 -0
  204. package/dist/types/participant.js +12 -0
  205. package/dist/types/streams.d.ts +8 -14
  206. package/dist/utils/mobx-setup.d.ts +1 -1
  207. package/dist/utils/mobx-setup.js +2 -2
  208. package/dist/wire/errorEnvelope.d.ts +11 -2
  209. package/dist/wire/errorEnvelope.js +14 -2
  210. package/dist/wire/frames.d.ts +90 -0
  211. package/dist/wire/frames.js +55 -1
  212. package/dist/wire/index.d.ts +4 -1
  213. package/dist/wire/index.js +20 -1
  214. package/dist/wire/protocol.d.ts +45 -0
  215. package/dist/wire/protocol.js +45 -0
  216. package/dist/wire/protocolVersion.d.ts +56 -0
  217. package/dist/wire/protocolVersion.js +63 -0
  218. package/docs/api-keys.md +4 -3
  219. package/docs/data-sources.md +12 -5
  220. package/docs/examples/existing-python-backend.md +3 -3
  221. package/docs/identity.md +4 -4
  222. package/docs/integration-guide.md +1 -1
  223. package/docs/react.md +1 -1
  224. package/docs/sessions.md +5 -7
  225. package/llms.txt +4 -2
  226. package/package.json +15 -13
  227. package/dist/client/index.d.ts +0 -36
  228. package/dist/client/index.js +0 -33
  229. package/dist/config/index.d.ts +0 -10
  230. package/dist/config/index.js +0 -12
  231. package/dist/interfaces/headless.d.ts +0 -95
  232. package/dist/interfaces/headless.js +0 -41
  233. package/dist/query/index.d.ts +0 -6
  234. package/dist/query/index.js +0 -5
  235. package/dist/realtime/index.d.ts +0 -10
  236. package/dist/realtime/index.js +0 -9
  237. package/dist/schema/plane.d.ts +0 -23
  238. package/dist/schema/plane.js +0 -19
  239. package/dist/server/next.d.ts +0 -51
  240. package/dist/server/next.js +0 -47
  241. package/dist/sync/OfflineFlush.d.ts +0 -9
  242. package/dist/sync/OfflineFlush.js +0 -22
  243. package/dist/sync/OfflineTransactionStore.d.ts +0 -37
  244. package/dist/sync/OfflineTransactionStore.js +0 -263
  245. package/dist/transactions/index.d.ts +0 -16
  246. package/dist/transactions/index.js +0 -7
package/docs/sessions.md CHANGED
@@ -53,7 +53,7 @@ return Response.json({ token }); // return ONLY the token to the browser
53
53
  A user session has **full data authority** within its org — no operation
54
54
  allowlist. It's the human acting as themselves.
55
55
 
56
- Build a browser `Ablo` client whose `getToken` fetches from that route, and pass
56
+ Point a browser `Ablo` client's `authEndpoint` at that route, and pass
57
57
  the **instance** to [`<AbloProvider>`](/react). The client fetches the token,
58
58
  opens the connection, and re-mints before expiry — your app writes no token
59
59
  plumbing:
@@ -67,10 +67,7 @@ import { schema } from '@/ablo.schema';
67
67
 
68
68
  const ablo = Ablo({
69
69
  schema,
70
- getToken: () =>
71
- fetch('/api/ablo-session', { method: 'POST' })
72
- .then((r) => r.json())
73
- .then((d) => d.token),
70
+ authEndpoint: '/api/ablo-session',
74
71
  });
75
72
 
76
73
  export function Providers({ children }: { children: React.ReactNode }) {
@@ -81,8 +78,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
81
78
  The client owns auth, the credential lifecycle, and the connection; the provider
82
79
  is the thin reactive binding over it (Stripe's `<Elements stripe={...}>` model).
83
80
  Build the client **once** at module scope — a new instance per render tears down
84
- the socket. `authEndpoint: '/api/ablo-session'` is accepted as sugar for the
85
- `getToken` fetch above if you prefer a URL.
81
+ the socket. Need custom headers or a
82
+ body on the exchange? `authEndpoint` also accepts an async resolver that
83
+ returns the token.
86
84
 
87
85
  ## Agent sessions (`rk_`)
88
86
 
package/llms.txt CHANGED
@@ -122,9 +122,11 @@ A schema is model fields and relations. Advanced schema helpers such as `mutable
122
122
 
123
123
  ## Storage Boundary
124
124
 
125
- `databaseUrl` is an OPTIONAL, server-only constructor option on `Ablo(...)`. It is NOT auto-read from the environment pass it EXPLICITLY to register your Postgres directly. Omit it when you expose a signed Data Source endpoint, or when trying Ablo against the hosted sandbox (apiKey only). A `DATABASE_URL` set for another tool (Prisma, Drizzle, docker-compose) is ignored unless you pass `databaseUrl` explicitly.
125
+ THE ONE PATH IS LOGICAL REPLICATION. In production every schema model is backed by YOUR OWN database, and Ablo connects to it by CONSUMING ITS WRITE-AHEAD LOG (logical replication) the Electric/PowerSync/Zero model. Ablo tails the WAL and fans changes out; YOUR APPLICATION CONTINUES TO OWN THE WRITE PATH (your own backend, your existing API). Ablo never runs DDL on, writes to, owns, or migrates your database. Connect once with `npx ablo connect` (prints the setup SQL: `wal_level=logical`, a publication, a `REPLICATION` role) then `npx ablo connect --register`. Validate with `npx ablo connect --check`. The role's privilege footprint is narrow but real a `REPLICATION`-attributed role that streams the WAL and `SELECT`s; do NOT describe it as "read-only" (a security reviewer will push back — it is a replication privilege, just tightly scoped).
126
126
 
127
- In production, every schema model is backed by YOUR OWN database. The PRIMARY path is the connection string: pass `databaseUrl` (most users already have a Postgres — often Prisma- or Drizzle-managed for auth/audit/log tables that are NOT in the Ablo schema; Ablo syncs a SUBSET of models against it). Most users do NOT run `ablo migrate` they ADOPT existing tables with `npx ablo pull` / `npx ablo check`, or keep managing tables with their own migration tool. Run `npx ablo migrate` only when Ablo should OWN the tables (it provisions the synced-model tables in your DB). The alternative to the connection string is a signed 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`); use it when database credentials must never leave your infrastructure (or for a local/private-range DB, which Ablo's cloud cannot reach over the network).
127
+ FALLBACK (only when the database can't grant a `REPLICATION` role, e.g. a locked-down managed DB): a signed Data Source endpoint that hands Ablo an ORM `adapter` (Drizzle default; Prisma and Kysely supported) — it owns the transaction, exactly-once idempotency, and outbox in ONE pass; your credentials never leave your infrastructure.
128
+
129
+ DEPRECATED — being removed, do NOT recommend or scaffold: the `databaseUrl` constructor option (the dial-in, where Ablo held a read/write connection and committed directly to your DB). The end-state constructor is `Ablo({ schema, apiKey })`; the database is connected out of band via `ablo connect`, not through `databaseUrl`. (Note: `ablo init` still defaults to `--storage direct` scaffolding `databaseUrl` — that default is itself slated to lead with `ablo connect`; prefer logical replication regardless of what `init` scaffolds.)
128
130
 
129
131
  GOTCHA the user WILL hit: `DATABASE_URL` must use a NON-superuser, NON-BYPASSRLS role (Ablo enforces row-level security; owner roles are rejected with `database_role_cannot_enforce_rls`). Neon's and Supabase's default dashboard connection strings use the database OWNER (e.g. `neondb_owner`) and are rejected. EASIEST: `npx ablo migrate` detects the unsafe role and creates the scoped one automatically from the user's machine (owner credential never reaches Ablo; new DATABASE_URL written to the env file). Manual alternative — create a scoped role first: `CREATE ROLE ablo_app LOGIN PASSWORD '...' NOSUPERUSER NOBYPASSRLS; GRANT CREATE, CONNECT ON DATABASE <db> TO ablo_app; GRANT CREATE, USAGE ON SCHEMA public TO ablo_app;` — then swap user/password into the same host/db string.
130
132
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/ablo",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "The Collaboration Layer For AI Agents",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -108,11 +108,6 @@
108
108
  "import": "./dist/server/index.js",
109
109
  "default": "./dist/server/index.js"
110
110
  },
111
- "./server/next": {
112
- "types": "./dist/server/next.d.ts",
113
- "import": "./dist/server/next.js",
114
- "default": "./dist/server/next.js"
115
- },
116
111
  "./webhooks": {
117
112
  "types": "./dist/webhooks/index.d.ts",
118
113
  "import": "./dist/webhooks/index.js",
@@ -121,7 +116,6 @@
121
116
  },
122
117
  "files": [
123
118
  "dist",
124
- "!dist/internal/**",
125
119
  "!dist/__type_probe.*",
126
120
  "docs/*.md",
127
121
  "docs/examples/*.md",
@@ -139,7 +133,9 @@
139
133
  "build": "npm run clean && tsc -p tsconfig.build.json && npm run build:cli",
140
134
  "build:cli": "tsup --config tsup.cli.config.ts",
141
135
  "typecheck:cli": "tsc -p tsconfig.cli.json",
142
- "pack:check": "npm_config_cache=${TMPDIR:-/tmp}/ablo-npm-cache npm pack --dry-run",
136
+ "prepack": "node scripts/strip-source-condition.mjs",
137
+ "postpack": "node scripts/restore-source-condition.mjs",
138
+ "pack:check": "node scripts/pack-check.mjs",
143
139
  "lint": "npm run lint:imports && npm run lint:errors && npm run lint:docs && npm run lint:mintlify",
144
140
  "build:docs": "node scripts/build-mintlify-docs.mjs",
145
141
  "lint:mintlify": "node scripts/build-mintlify-docs.mjs --check",
@@ -147,6 +143,7 @@
147
143
  "generate:errors": "tsx scripts/generate-error-docs.mts",
148
144
  "lint:errors": "tsx scripts/check-error-docs.mts",
149
145
  "lint:docs": "node scripts/check-doc-drift.mjs",
146
+ "lint:eslint": "eslint . --cache",
150
147
  "lint:pkg": "publint",
151
148
  "prepublishOnly": "npm run build && npm run lint:pkg",
152
149
  "check:dist": "node scripts/check-dist-fresh.mjs",
@@ -191,7 +188,8 @@
191
188
  },
192
189
  "peerDependencies": {
193
190
  "react": "^19.0.0",
194
- "drizzle-orm": ">=0.44.0"
191
+ "drizzle-orm": ">=0.44.0",
192
+ "ai": "^6.0.0"
195
193
  },
196
194
  "peerDependenciesMeta": {
197
195
  "react": {
@@ -199,6 +197,9 @@
199
197
  },
200
198
  "drizzle-orm": {
201
199
  "optional": true
200
+ },
201
+ "ai": {
202
+ "optional": true
202
203
  }
203
204
  },
204
205
  "dependencies": {
@@ -211,21 +212,21 @@
211
212
  "devDependencies": {
212
213
  "@ai-sdk/provider": "^3.0.0",
213
214
  "@clack/prompts": "^0.11.0",
215
+ "@jest/globals": "^29.7.0",
214
216
  "@prisma/client": "^7.3.0",
215
217
  "@types/jest": "^29.5.0",
216
218
  "@types/node": "^22.0.0",
217
219
  "@types/react": "^19.0.0",
218
220
  "@types/uuid": "^10.0.0",
219
221
  "@testing-library/react": "^16.0.0",
220
- "@testing-library/jest-dom": "^6.6.0",
221
222
  "ai": "^6.0.0",
222
223
  "drizzle-orm": "^0.45.2",
224
+ "eslint-plugin-react-hooks": "^7.0.1",
223
225
  "fake-indexeddb": "^6.0.0",
224
226
  "fast-check": "^3.0.0",
227
+ "globals": "^16.5.0",
225
228
  "jest": "^29.7.0",
226
229
  "jest-environment-jsdom": "^29.7.0",
227
- "jiti": "^2.7.0",
228
- "mobx": "^6.13.7",
229
230
  "picocolors": "^1.1.0",
230
231
  "postgres": "^3.4.0",
231
232
  "react": "^19.0.0",
@@ -233,8 +234,9 @@
233
234
  "ts-jest": "^29.4.0",
234
235
  "ts-morph": "^26.0.0",
235
236
  "tsup": "^8.0.0",
237
+ "tsx": "^4.19.0",
236
238
  "typescript": "^5.8.3",
237
239
  "publint": "^0.3.21",
238
- "yjs": "^13.6.29"
240
+ "ws": "^8.18.0"
239
241
  }
240
242
  }
@@ -1,36 +0,0 @@
1
- /**
2
- * @abloatai/ablo/client — Consumer API
3
- *
4
- * The one-liner entry point for external consumers.
5
- *
6
- * `Ablo({ apiKey })` is the stateless HTTP API client. Add `schema`
7
- * when you want the realtime sync engine with typed model proxies.
8
- *
9
- * ```ts
10
- * import { Ablo } from '@abloatai/ablo/client';
11
- * import { schema } from './schema';
12
- *
13
- * const ablo = Ablo({
14
- * schema,
15
- * apiKey: process.env.ABLO_API_KEY,
16
- * });
17
- *
18
- * const reports = ablo.weatherReports.list({ where: { status: 'pending' } });
19
- * await ablo.weatherReports.create({ location: 'Stockholm', status: 'pending' });
20
- * ```
21
- *
22
- * For headless agents (workers, bots), pass the same schema and an API key
23
- * scoped for that server runtime:
24
- *
25
- * ```ts
26
- * const bot = Ablo({
27
- * schema,
28
- * apiKey: process.env.ABLO_API_KEY,
29
- * });
30
- * ```
31
- */
32
- export { Ablo, computeFKDepthPriority, type AbloOptions, type InternalAbloOptions, type ClaimedOptions, type IfClaimedPolicy, type ClaimWaitOptions, type LocalCountOptions, type LocalReadOptions, type ModelListScope, type ServerReadOptions, type ModelOperations, type ModelReadOptions, } from './Ablo.js';
33
- export { ABLO_DEFAULT_BASE_URL, ABLO_HOSTED_API_DOMAIN, ABLO_HOSTED_HTTP_BASE_URL, normalizeAbloHostedBaseUrl, } from './auth.js';
34
- export type { AbloPersistence } from './persistence.js';
35
- export type { AbloApi, AbloApiClientOptions, AbloApiClaims, Capability, CapabilityCreateOptions, CapabilityParticipantKind, CapabilityRecord, CapabilityResource, CapabilityRevocation, CapabilityScope, } from './ApiClient.js';
36
- export type { EngineParticipant, JoinedParticipant, ParticipantJoinOptions, ParticipantManager, ParticipantScope, ParticipantStatus, ScopedClaims, ScopedPresence, } from '../sync/participants.js';
@@ -1,33 +0,0 @@
1
- /**
2
- * @abloatai/ablo/client — Consumer API
3
- *
4
- * The one-liner entry point for external consumers.
5
- *
6
- * `Ablo({ apiKey })` is the stateless HTTP API client. Add `schema`
7
- * when you want the realtime sync engine with typed model proxies.
8
- *
9
- * ```ts
10
- * import { Ablo } from '@abloatai/ablo/client';
11
- * import { schema } from './schema';
12
- *
13
- * const ablo = Ablo({
14
- * schema,
15
- * apiKey: process.env.ABLO_API_KEY,
16
- * });
17
- *
18
- * const reports = ablo.weatherReports.list({ where: { status: 'pending' } });
19
- * await ablo.weatherReports.create({ location: 'Stockholm', status: 'pending' });
20
- * ```
21
- *
22
- * For headless agents (workers, bots), pass the same schema and an API key
23
- * scoped for that server runtime:
24
- *
25
- * ```ts
26
- * const bot = Ablo({
27
- * schema,
28
- * apiKey: process.env.ABLO_API_KEY,
29
- * });
30
- * ```
31
- */
32
- export { Ablo, computeFKDepthPriority, } from './Ablo.js';
33
- export { ABLO_DEFAULT_BASE_URL, ABLO_HOSTED_API_DOMAIN, ABLO_HOSTED_HTTP_BASE_URL, normalizeAbloHostedBaseUrl, } from './auth.js';
@@ -1,10 +0,0 @@
1
- /**
2
- * @abloatai/ablo/config — App initialization
3
- *
4
- * One-time setup at app boot. Provides DI interface types
5
- * and the initSyncEngine() function to wire real implementations.
6
- */
7
- export { initSyncEngine, resetSyncEngine, isSyncEngineInitialized } from '../context.js';
8
- export { noopLogger, noopObservability, noopAnalytics, browserOnlineStatus, defaultSessionErrorDetector, emptyConfig, type SyncEngineContext, } from '../SyncEngineContext.js';
9
- export type { SyncEngineConfig, SyncLogger, SyncObservabilityProvider, SyncAnalytics, MutationExecutor, MutationDispatcher, SessionErrorDetector, OnlineStatusProvider, CommitResult, MutationOperation, BreadcrumbLevel, SyncBreadcrumbCategory, TransactionFailureDetails, BootstrapFailureDetails, WebSocketErrorDetails, RollbackDetails, SpanAttributes, } from '../interfaces/index.js';
10
- export { SyncSessionError } from '../errors.js';
@@ -1,12 +0,0 @@
1
- /**
2
- * @abloatai/ablo/config — App initialization
3
- *
4
- * One-time setup at app boot. Provides DI interface types
5
- * and the initSyncEngine() function to wire real implementations.
6
- */
7
- // Context lifecycle
8
- export { initSyncEngine, resetSyncEngine, isSyncEngineInitialized } from '../context.js';
9
- // Context type + no-op defaults (for testing or gradual adoption)
10
- export { noopLogger, noopObservability, noopAnalytics, browserOnlineStatus, defaultSessionErrorDetector, emptyConfig, } from '../SyncEngineContext.js';
11
- // Errors
12
- export { SyncSessionError } from '../errors.js';
@@ -1,95 +0,0 @@
1
- /**
2
- * Headless DI interfaces — the three abstractions createSyncEngine needs
3
- * to run in Node.js without browser globals.
4
- *
5
- * STATUS: Type stubs only. No implementation yet. These interfaces are
6
- * the deliverable of Path C Phase 1. When all three have both a browser
7
- * implementation and a Node implementation, the SDK is truly headless.
8
- *
9
- * Usage (once implemented):
10
- *
11
- * // Browser (default — no DI needed, uses browser globals)
12
- * const engine = createSyncEngine({ url, userId, organizationId });
13
- *
14
- * // Node.js / agent / sidecar (headless — DI overrides)
15
- * import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
16
- * const engine = createSyncEngine({
17
- * url, userId, organizationId,
18
- * storage: inMemoryStorage(),
19
- * network: alwaysOnline(),
20
- * });
21
- *
22
- * The `transport` override is optional because Node 22 has a built-in
23
- * global `WebSocket` class. It's included for testing (mock WebSocket)
24
- * and for environments where the built-in WebSocket isn't available
25
- * (Node 20, Deno, Bun — each has its own WebSocket story).
26
- *
27
- * See also: apps/sync-server/src/sdk-headless-entrypoint.test.ts
28
- * (the skipped tests that become the acceptance tests for Phase 1)
29
- */
30
- /** A record in an object store. */
31
- export type StorageRecord = Record<string, unknown> & {
32
- id: string;
33
- };
34
- /** An object store — analogous to an IDBObjectStore. */
35
- export interface ObjectStore {
36
- get(id: string): Promise<StorageRecord | undefined>;
37
- getAll(): Promise<StorageRecord[]>;
38
- getAllFromIndex(indexName: string, value: string): Promise<StorageRecord[]>;
39
- put(record: StorageRecord): Promise<void>;
40
- delete(id: string): Promise<void>;
41
- clear(): Promise<void>;
42
- }
43
- /** A database — analogous to an IDBDatabase. */
44
- export interface StorageDatabase {
45
- getStore(name: string): ObjectStore | undefined;
46
- close(): void;
47
- }
48
- /**
49
- * Opens or creates a named database.
50
- *
51
- * Browser: wraps indexedDB.open(name, version)
52
- * Node: returns an in-memory database backed by Maps
53
- */
54
- export interface StorageProvider {
55
- open(name: string, version?: number): Promise<StorageDatabase>;
56
- delete(name: string): Promise<void>;
57
- }
58
- export interface NetworkProvider {
59
- /** Current online status. */
60
- isOnline(): boolean;
61
- /**
62
- * Subscribe to online/offline transitions. Returns an unsubscribe function.
63
- * The callback receives `true` when coming online, `false` when going offline.
64
- *
65
- * Browser: listens for window 'online'/'offline' events + visibilitychange
66
- * Node: never fires (always online)
67
- * Test: fires when the test calls setOnline(true/false)
68
- */
69
- onStatusChange(callback: (online: boolean) => void): () => void;
70
- }
71
- /** Minimal WebSocket interface that SyncWebSocket uses. */
72
- export interface WebSocketLike {
73
- readonly readyState: number;
74
- readonly bufferedAmount: number;
75
- send(data: string | ArrayBuffer): void;
76
- close(code?: number, reason?: string): void;
77
- ping?(): void;
78
- addEventListener(type: 'open', handler: () => void): void;
79
- addEventListener(type: 'close', handler: (event: {
80
- code: number;
81
- reason: string;
82
- }) => void): void;
83
- addEventListener(type: 'message', handler: (event: {
84
- data: unknown;
85
- }) => void): void;
86
- addEventListener(type: 'error', handler: (event: unknown) => void): void;
87
- removeEventListener(type: string, handler: (...args: unknown[]) => void): void;
88
- }
89
- export interface TransportProvider {
90
- /**
91
- * Create a WebSocket connection to the given URL.
92
- * Returns an object satisfying the WebSocketLike interface.
93
- */
94
- connect(url: string): WebSocketLike;
95
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Headless DI interfaces — the three abstractions createSyncEngine needs
3
- * to run in Node.js without browser globals.
4
- *
5
- * STATUS: Type stubs only. No implementation yet. These interfaces are
6
- * the deliverable of Path C Phase 1. When all three have both a browser
7
- * implementation and a Node implementation, the SDK is truly headless.
8
- *
9
- * Usage (once implemented):
10
- *
11
- * // Browser (default — no DI needed, uses browser globals)
12
- * const engine = createSyncEngine({ url, userId, organizationId });
13
- *
14
- * // Node.js / agent / sidecar (headless — DI overrides)
15
- * import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
16
- * const engine = createSyncEngine({
17
- * url, userId, organizationId,
18
- * storage: inMemoryStorage(),
19
- * network: alwaysOnline(),
20
- * });
21
- *
22
- * The `transport` override is optional because Node 22 has a built-in
23
- * global `WebSocket` class. It's included for testing (mock WebSocket)
24
- * and for environments where the built-in WebSocket isn't available
25
- * (Node 20, Deno, Bun — each has its own WebSocket story).
26
- *
27
- * See also: apps/sync-server/src/sdk-headless-entrypoint.test.ts
28
- * (the skipped tests that become the acceptance tests for Phase 1)
29
- */
30
- export {};
31
- // ── Factory functions (to be implemented in Phase 1) ─────────────────────
32
- //
33
- // These will be the public API that headless consumers import:
34
- //
35
- // import { inMemoryStorage, alwaysOnline } from '@abloatai/ablo/headless';
36
- //
37
- // Stubs below show the intended signatures. Implementation is Phase 1 work.
38
- // export function inMemoryStorage(): StorageProvider { ... }
39
- // export function alwaysOnline(): NetworkProvider { ... }
40
- // export function nodeWebSocket(): TransportProvider { ... }
41
- // export function controllableNetwork(): NetworkProvider & { setOnline(v: boolean): void } { ... }
@@ -1,6 +0,0 @@
1
- /**
2
- * Query module barrel — re-exports the public surface for convenience.
3
- * See types.ts and client.ts for the actual definitions.
4
- */
5
- export type { Query, QueryBatch, QueryBatchResult } from './types.js';
6
- export { postQuery, type PostQueryOptions } from './client.js';
@@ -1,5 +0,0 @@
1
- /**
2
- * Query module barrel — re-exports the public surface for convenience.
3
- * See types.ts and client.ts for the actual definitions.
4
- */
5
- export { postQuery } from './client.js';
@@ -1,10 +0,0 @@
1
- /**
2
- * Internal compatibility entrypoint for the schema-powered realtime client.
3
- *
4
- * Use this build for applications that need typed model proxies,
5
- * subscriptions, presence, offline queueing, and a long-lived WebSocket.
6
- */
7
- export { Ablo, computeFKDepthPriority } from '../client/Ablo.js';
8
- export type { AbloOptions, InternalAbloOptions, LocalCountOptions, LocalReadOptions, ModelListScope, ServerReadOptions, ModelOperations, } from '../client/Ablo.js';
9
- import { Ablo } from '../client/Ablo.js';
10
- export default Ablo;
@@ -1,9 +0,0 @@
1
- /**
2
- * Internal compatibility entrypoint for the schema-powered realtime client.
3
- *
4
- * Use this build for applications that need typed model proxies,
5
- * subscriptions, presence, offline queueing, and a long-lived WebSocket.
6
- */
7
- export { Ablo, computeFKDepthPriority } from '../client/Ablo.js';
8
- import { Ablo } from '../client/Ablo.js';
9
- export default Ablo;
@@ -1,23 +0,0 @@
1
- /**
2
- * Database PLANE — which database a model's rows live in. A sibling axis to
3
- * `tenancy` (which says how rows are isolated *within* a database):
4
- *
5
- * - `tenant` — the tenant data plane. For a BYO/dedicated customer this is
6
- * THEIR database; provisioning emits these tables there.
7
- * - `control` — Ablo's control plane (the sync log, attribution, audit, …).
8
- * Never emitted into a customer DB; lives only in Ablo's own DB.
9
- *
10
- * P1 of the sync-delta decomposition (`docs/plans/sync-delta-zod-decomposition.md`):
11
- * declaring the boundary lets BYO provisioning *derive* "what a customer DB gets"
12
- * (`plane === 'tenant'`) instead of hand-coding it. Defaults to `tenant` —
13
- * today every `defineSchema` model is the customer's own data; only Ablo's
14
- * internal tables (once modeled in P2) declare `control`.
15
- */
16
- import { z } from 'zod';
17
- export declare const planeSchema: z.ZodEnum<{
18
- tenant: "tenant";
19
- control: "control";
20
- }>;
21
- export type SchemaPlane = z.infer<typeof planeSchema>;
22
- /** Default plane for a model that doesn't declare one — the tenant data plane. */
23
- export declare const DEFAULT_PLANE: SchemaPlane;
@@ -1,19 +0,0 @@
1
- /**
2
- * Database PLANE — which database a model's rows live in. A sibling axis to
3
- * `tenancy` (which says how rows are isolated *within* a database):
4
- *
5
- * - `tenant` — the tenant data plane. For a BYO/dedicated customer this is
6
- * THEIR database; provisioning emits these tables there.
7
- * - `control` — Ablo's control plane (the sync log, attribution, audit, …).
8
- * Never emitted into a customer DB; lives only in Ablo's own DB.
9
- *
10
- * P1 of the sync-delta decomposition (`docs/plans/sync-delta-zod-decomposition.md`):
11
- * declaring the boundary lets BYO provisioning *derive* "what a customer DB gets"
12
- * (`plane === 'tenant'`) instead of hand-coding it. Defaults to `tenant` —
13
- * today every `defineSchema` model is the customer's own data; only Ablo's
14
- * internal tables (once modeled in P2) declare `control`.
15
- */
16
- import { z } from 'zod';
17
- export const planeSchema = z.enum(['tenant', 'control']);
18
- /** Default plane for a model that doesn't declare one — the tenant data plane. */
19
- export const DEFAULT_PLANE = 'tenant';
@@ -1,51 +0,0 @@
1
- /**
2
- * `@abloatai/ablo/server/next` — mount Ablo's HTTP surface in a consumer's
3
- * Next.js App Router, the Better-Auth `toNextJsHandler` way:
4
- *
5
- * ```ts
6
- * // app/api/ablo/[...all]/route.ts
7
- * import { toAbloHandler } from '@abloatai/ablo/server/next';
8
- * import { ablo } from '@/lib/ablo'; // your configured Ablo HTTP app
9
- * export const { GET, POST } = toAbloHandler(ablo);
10
- * ```
11
- *
12
- * Ablo's HTTP endpoints (bootstrap / query / the commit fallback) are stateless
13
- * request→response, so they drop straight into a route handler. The realtime
14
- * WebSocket channel is deliberately NOT served here — it is a persistent
15
- * connection a serverless route handler cannot host, and stays an Ablo-hosted
16
- * (or long-running Node) service. That is the HTTP/WSS split the architecture
17
- * draws on purpose (see docs/plans/sync-gateway-audit-log-architecture.md).
18
- *
19
- * The package owns this MOUNT PRIMITIVE; the host owns the infra-bound app it
20
- * wraps (the Hono app holding the Hub + routes + auth). Mirrors Better Auth,
21
- * where `betterAuth()` builds the `.handler` and `toNextJsHandler` just maps it
22
- * onto the HTTP verbs.
23
- */
24
- /**
25
- * Anything that resolves a Web `Request` to a `Response` — e.g. a Hono app's
26
- * `.fetch`, a Better-Auth-style `{ handler }`, or a bare function.
27
- */
28
- export type AbloFetchHandler = (request: Request) => Response | Promise<Response>;
29
- /** The shapes `toAbloHandler` accepts: a Hono-style `{ fetch }`, a
30
- * `{ handler }`, or a bare `(Request) => Response`. */
31
- export type AbloHttpApp = {
32
- readonly fetch: AbloFetchHandler;
33
- } | {
34
- readonly handler: AbloFetchHandler;
35
- } | AbloFetchHandler;
36
- /** The route-handler object a Next.js App Router `route.ts` re-exports. One
37
- * handler bound to every method the `[...all]` catch-all may receive. */
38
- export interface AbloRouteHandlers {
39
- readonly GET: AbloFetchHandler;
40
- readonly POST: AbloFetchHandler;
41
- readonly PATCH: AbloFetchHandler;
42
- readonly PUT: AbloFetchHandler;
43
- readonly DELETE: AbloFetchHandler;
44
- readonly OPTIONS: AbloFetchHandler;
45
- }
46
- /**
47
- * Wrap a configured Ablo HTTP app into Next.js App Router route handlers.
48
- * Mirrors Better Auth's `toNextJsHandler` — accepts the app's `fetch`/`handler`
49
- * (or a bare function) and returns one handler per verb.
50
- */
51
- export declare function toAbloHandler(app: AbloHttpApp): AbloRouteHandlers;
@@ -1,47 +0,0 @@
1
- /**
2
- * `@abloatai/ablo/server/next` — mount Ablo's HTTP surface in a consumer's
3
- * Next.js App Router, the Better-Auth `toNextJsHandler` way:
4
- *
5
- * ```ts
6
- * // app/api/ablo/[...all]/route.ts
7
- * import { toAbloHandler } from '@abloatai/ablo/server/next';
8
- * import { ablo } from '@/lib/ablo'; // your configured Ablo HTTP app
9
- * export const { GET, POST } = toAbloHandler(ablo);
10
- * ```
11
- *
12
- * Ablo's HTTP endpoints (bootstrap / query / the commit fallback) are stateless
13
- * request→response, so they drop straight into a route handler. The realtime
14
- * WebSocket channel is deliberately NOT served here — it is a persistent
15
- * connection a serverless route handler cannot host, and stays an Ablo-hosted
16
- * (or long-running Node) service. That is the HTTP/WSS split the architecture
17
- * draws on purpose (see docs/plans/sync-gateway-audit-log-architecture.md).
18
- *
19
- * The package owns this MOUNT PRIMITIVE; the host owns the infra-bound app it
20
- * wraps (the Hono app holding the Hub + routes + auth). Mirrors Better Auth,
21
- * where `betterAuth()` builds the `.handler` and `toNextJsHandler` just maps it
22
- * onto the HTTP verbs.
23
- */
24
- function resolveFetch(app) {
25
- if (typeof app === 'function')
26
- return app;
27
- if ('fetch' in app)
28
- return app.fetch;
29
- return app.handler;
30
- }
31
- /**
32
- * Wrap a configured Ablo HTTP app into Next.js App Router route handlers.
33
- * Mirrors Better Auth's `toNextJsHandler` — accepts the app's `fetch`/`handler`
34
- * (or a bare function) and returns one handler per verb.
35
- */
36
- export function toAbloHandler(app) {
37
- const fetch = resolveFetch(app);
38
- const handler = (request) => fetch(request);
39
- return {
40
- GET: handler,
41
- POST: handler,
42
- PATCH: handler,
43
- PUT: handler,
44
- DELETE: handler,
45
- OPTIONS: handler,
46
- };
47
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * OfflineFlush — Replays queued offline mutations on reconnect.
3
- *
4
- * SDK-generic version: delegates to MutationDispatcher from context.
5
- */
6
- export declare function flushOfflineQueueOnce(): Promise<{
7
- processed: number;
8
- failed: number;
9
- }>;
@@ -1,22 +0,0 @@
1
- /**
2
- * OfflineFlush — Replays queued offline mutations on reconnect.
3
- *
4
- * SDK-generic version: delegates to MutationDispatcher from context.
5
- */
6
- import { OfflineTransactionStore } from './OfflineTransactionStore.js';
7
- import { getContext } from '../context.js';
8
- let _offlineTxStore = null;
9
- function getOfflineTxStore() {
10
- if (!_offlineTxStore) {
11
- _offlineTxStore = new OfflineTransactionStore();
12
- }
13
- return _offlineTxStore;
14
- }
15
- export async function flushOfflineQueueOnce() {
16
- const store = getOfflineTxStore();
17
- await store.init();
18
- const dispatcher = getContext().mutationDispatcher;
19
- return store.flush(async (tx) => {
20
- await dispatcher.dispatch(tx.opName, tx.request.variables || {});
21
- });
22
- }
@@ -1,37 +0,0 @@
1
- export declare enum Priority {
2
- CRITICAL = 0,
3
- HIGH = 1,
4
- NORMAL = 2,
5
- LOW = 3
6
- }
7
- export type OfflineTransaction = {
8
- id: string;
9
- opName: string;
10
- priority: Priority;
11
- createdAt: number;
12
- dependsOn?: string[];
13
- request: {
14
- gqlId?: string;
15
- document?: string;
16
- variables?: Record<string, any>;
17
- headers?: Record<string, string>;
18
- method?: string;
19
- url?: string;
20
- body?: any;
21
- };
22
- };
23
- export declare class OfflineTransactionStore {
24
- private db;
25
- private key;
26
- init(): Promise<void>;
27
- enqueue(tx: Omit<OfflineTransaction, 'createdAt'>): Promise<string>;
28
- listAll(): Promise<OfflineTransaction[]>;
29
- getOptimizedSyncOrder(): Promise<OfflineTransaction[]>;
30
- remove(id: string): Promise<void>;
31
- clear(): Promise<void>;
32
- flush(processor: (tx: OfflineTransaction) => Promise<void>): Promise<{
33
- processed: number;
34
- failed: number;
35
- }>;
36
- }
37
- export declare const offlineTxStore: OfflineTransactionStore;