@abloatai/ablo 0.48.0 → 0.50.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 (68) hide show
  1. package/AGENTS.md +2 -2
  2. package/CHANGELOG.md +144 -2
  3. package/README.md +12 -12
  4. package/dist/ai-sdk.d.ts +11 -0
  5. package/dist/ai-sdk.d.ts.map +1 -1
  6. package/dist/ai-sdk.js +15 -0
  7. package/dist/ai-sdk.js.map +1 -1
  8. package/dist/batching.d.ts +7 -0
  9. package/dist/batching.d.ts.map +1 -0
  10. package/dist/batching.js +7 -0
  11. package/dist/batching.js.map +1 -0
  12. package/dist/client.d.ts +12 -0
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +9 -0
  15. package/dist/client.js.map +1 -1
  16. package/dist/context/await.d.ts +10 -0
  17. package/dist/context/await.d.ts.map +1 -0
  18. package/dist/context/await.js +33 -0
  19. package/dist/context/await.js.map +1 -0
  20. package/dist/context/evidence.d.ts +11 -0
  21. package/dist/context/evidence.d.ts.map +1 -0
  22. package/dist/context/evidence.js +53 -0
  23. package/dist/context/evidence.js.map +1 -0
  24. package/dist/context/sources.d.ts +21 -0
  25. package/dist/context/sources.d.ts.map +1 -0
  26. package/dist/context/sources.js +36 -0
  27. package/dist/context/sources.js.map +1 -0
  28. package/dist/context.d.ts +22 -0
  29. package/dist/context.d.ts.map +1 -0
  30. package/dist/context.js +33 -0
  31. package/dist/context.js.map +1 -0
  32. package/dist/coordination.d.ts +8 -0
  33. package/dist/coordination.d.ts.map +1 -1
  34. package/dist/index.d.ts +8 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +7 -0
  37. package/dist/index.js.map +1 -1
  38. package/docs/agent-messaging.md +2 -2
  39. package/docs/api.md +2 -2
  40. package/docs/concurrency-convention.md +82 -268
  41. package/docs/context.md +170 -0
  42. package/docs/coordination.md +178 -905
  43. package/docs/data-sources.md +32 -1
  44. package/docs/examples/ai-sdk-tool.md +4 -0
  45. package/docs/guarantees.md +29 -21
  46. package/docs/how-it-works.md +10 -40
  47. package/docs/identity.md +48 -0
  48. package/docs/index.md +2 -2
  49. package/docs/integrations.md +4 -0
  50. package/docs/migration.md +46 -443
  51. package/docs/operating-on-your-database.md +29 -36
  52. package/examples/README.md +24 -0
  53. package/examples/agent-turn.ts +37 -0
  54. package/examples/data-source/ablo-driver.ts +4 -4
  55. package/examples/data-source/customer-server.ts +9 -4
  56. package/examples/data-source/schema.ts +1 -1
  57. package/examples/expensive-agent-turn.ts +76 -0
  58. package/examples/quickstart.ts +9 -7
  59. package/package.json +19 -5
  60. package/docs/internal/README.md +0 -18
  61. package/docs/internal/agent-fleet-coordination-design.md +0 -171
  62. package/docs/internal/commit-identifiers.md +0 -91
  63. package/docs/internal/concurrency-open-decisions.md +0 -37
  64. package/docs/internal/data-source-reverse-channel.md +0 -147
  65. package/docs/internal/per-field-conflict-detection.md +0 -165
  66. package/docs/internal/postgres-replication.md +0 -64
  67. package/docs/internal/serializable-schema.md +0 -119
  68. package/docs/internal/structure.md +0 -36
package/AGENTS.md CHANGED
@@ -11,7 +11,7 @@ Don't hand-write the integration. Run the CLI; it generates the current-API sche
11
11
  - **Read the docs for THIS version:** `npx ablo docs` lists every page, `npx ablo docs <page>` prints one. They ship inside the installed package, so they describe the code in `node_modules` and work with no network. Read them instead of a docs URL — a website describes the newest release, so against a pinned version it will hand you a call your package doesn't have (`retrieve`/`list` replaced `get`/`getAll`/`getCount` in 0.35.0).
12
12
  - **Scaffold:** `npx ablo init --yes` — flag-driven, never prompts. Override defaults with `--framework <nextjs|vite|remix|vanilla>`, `--auth <apikey|…>`, `--no-agent`, `--no-pull`, `--no-install`, `--no-login`. (Plain `ablo init` needs a TTY and will **HANG** in an agent/CI run — always pass `--yes`.)
13
13
  - **Auth:** set `ABLO_API_KEY` in the environment. Do **NOT** run `ablo login` — it opens a browser device flow and blocks an agent.
14
- - **Connect your database — logical replication (the primary path):** `npx ablo connect` prints the setup SQL (`wal_level=logical`, a publication, a `REPLICATION` role); `npx ablo connect register` registers the source with Ablo in one step. Ablo **consumes your Postgres' logical-replication stream** it never runs DDL on, writes to, owns, or migrates your database, and your application keeps the write path. Registration **is** the enable; there is no tier or flag to pick. (Ablo hosts only the transaction log + coordination, never your rows.)
14
+ - **Connect your database — logical replication (the primary path):** `npx ablo connect apply --url <postgres-url>` provisions scoped roles and replication using the admin credential supplied for that command. Model writes then go through Ablo and land in your Postgres; its change stream confirms them. Ablo does not own your rows or run application-schema migrations. Your ORM remains responsible for tables, columns, and constraints.
15
15
  - **Fallback — signed Data Source endpoint** (DB can't grant a `REPLICATION` role): the generated `ablo/data-source.ts` exposes one route; Ablo sends signed requests and your app touches its own DB. **Only in this mode** does `npx ablo migrate` provision the adapter's bookkeeping tables (`ablo_outbox`, `ablo_idempotency`) plus your Ablo models — it does **not** touch your other tables. Keep your own migrations (drizzle-kit / prisma migrate) for auth and anything outside the Ablo schema.
16
16
  - **No database yet?** Run `npx ablo dev --no-watch --branch <name>` to create an isolated non-root branch and obtain its expiring `sk_` credential. The branch uses a throwaway hosted data plane; Production remains the protected root. There is no shared Sandbox mode.
17
17
  - **Adopt an existing DB schema:** `npx ablo pull prisma [path]` / `pull drizzle <module>` (lossless) or `pull` (live DB, lossy). Writes `ablo/schema.ts`.
@@ -21,7 +21,7 @@ Don't hand-write the integration. Run the CLI; it generates the current-API sche
21
21
 
22
22
  When you use the signed-endpoint fallback, the generated `ablo/data-source.ts` is the whole endpoint and needs no hand-editing: `dataSourceNext({ schema, apiKey, adapter: prismaDataSource(prisma, schema) })` (or `drizzleDataSource(db, schema)`). The adapter owns commit / idempotency / outbox.
23
23
 
24
- **Working on a real database?** Ablo never runs DDL, migrates, or drops on your database, and every model write is claim-checked and reversible in the log so reads and `ablo.<model>.update(...)` are yours to run freely, while raw DDL (`ALTER TABLE …`) and a `--yes` cutover belong to a human. When you're unsure whether a write fits, `npx ablo check` reports the live column-by-column fit read-only, before anything runs. Full sorting rule: [Operating on Your Database](./docs/operating-on-your-database.md).
24
+ **Working on a real database?** Plain model writes are last-write-wins when no active claim applies. Use a functional update, a held claim, or `readAt` when a result depends on an earlier value. Reads are safe to inspect; raw application DDL (`ALTER TABLE …`) and a `--yes` connection cutover belong to a human. When you're unsure whether a write fits, `npx ablo check` reports the live column-by-column fit read-only, before anything runs. Full sorting rule: [Operating on Your Database](./docs/operating-on-your-database.md).
25
25
 
26
26
  ## Rule
27
27
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,127 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.50.0
4
+
5
+ ### Context can travel from reads to a model and back to a write
6
+
7
+ `context({ ablo, data })` brings together the information an action needs. It
8
+ awaits the values the application selected, returns them as typed `ctx.data`,
9
+ and carries exact Ablo rows into `ctx.reads` for the write that follows.
10
+
11
+ ```ts
12
+ const ctx = await context({
13
+ ablo,
14
+ data: {
15
+ task: ablo.tasks.get({ id: taskId }),
16
+ documents: ablo.documents.list({ where: { taskId } }),
17
+ memory: loadMemories(taskId),
18
+ },
19
+ });
20
+
21
+ await ablo.tasks.update({
22
+ id: taskId,
23
+ data: result,
24
+ reads: ctx.reads,
25
+ });
26
+ ```
27
+
28
+ If an included Ablo row moved while the caller was thinking, the write is
29
+ refused. External memory, retrieval, extraction, and conversation values pass
30
+ through without acquiring that guarantee. `ctx.sources` keeps the difference
31
+ visible, including a `mixed` result when one value contains both kinds.
32
+
33
+ The optional `contextMessage()` formatter produces a user message for AI SDK.
34
+ Ablo does not take over the model loop, history, token policy, search, or memory.
35
+ The helper is a standalone `@abloatai/ablo/context` export, so `context` remains
36
+ available as a schema model name.
37
+
38
+ ## 0.49.0
39
+
40
+ ### An agent can tell Ablo what it read before it writes
41
+
42
+ An agent reads a row, spends a model call deciding what to do, and then writes.
43
+ Another agent can change that row while the model is still thinking, and the
44
+ write lands anyway, on top of a decision that is no longer true. Pass the rows
45
+ the decision was based on:
46
+
47
+ ```ts
48
+ const task = await ablo.tasks.get({ id: taskId });
49
+ await ablo.tasks.update({
50
+ id: task.id,
51
+ data: { status: 'done', result: `Completed: ${task.title}` },
52
+ reads: [task],
53
+ });
54
+ ```
55
+
56
+ If either row moved while the agent was thinking, the write is refused instead
57
+ of overwriting. The rows carry that evidence themselves, so there is nothing to
58
+ set up around your agent and no wrapper to run it inside. One row or several,
59
+ the same row you are writing or a different one, all use `reads`. Rows an agent
60
+ read without passing stay out of it, so `reads` says what the decision rested on
61
+ rather than everything the agent happened to look at.
62
+
63
+ `idempotencyKey` stays a separate option. It gives a write one stable identity if
64
+ the agent retries, which is a different question from what the write assumed.
65
+
66
+ ### A claim holds while an agent thinks
67
+
68
+ Agents that take minutes per turn can now hold work safely. A claim waits its
69
+ turn or skips, expires on its own, and keeps itself alive with a heartbeat while
70
+ the agent works.
71
+
72
+ If an agent loses its claim during a model call, its final write is refused. Two
73
+ agents cannot both believe they own the same task and both write, and a slow
74
+ agent cannot land its answer on top of whoever picked the work up after it. Ablo
75
+ decides who holds the claim, so an agent cannot assert one it does not have.
76
+
77
+ ### An agent can read back what it committed
78
+
79
+ Ask what happened to a write, using the same key the agent wrote with:
80
+
81
+ ```ts
82
+ const record = await ablo.commits.get({ id: commitId });
83
+ ```
84
+
85
+ The answer says who committed, what they intended, whether it is confirmed, and
86
+ which claim protected it. `commits.list` walks the history a page at a time, so
87
+ one agent can review what another already did before repeating it.
88
+
89
+ What an agent sent is not kept. Prompts, reasoning, and your customers' row
90
+ values are removed before the record is stored, so reading history back never
91
+ replays an agent's inputs. Records are kept for 90 days, and permanently for a
92
+ database you connected.
93
+
94
+ ### An agent can check what its key allows before it acts
95
+
96
+ An agent holding a key can now ask what that key permits and get the answer from
97
+ Ablo, whether it keeps a connection open or calls over HTTP for a single turn.
98
+ An agent that mints a narrower key for a sub-task can confirm what it handed
99
+ over.
100
+
101
+ ### A refused action says which permission was missing
102
+
103
+ When Ablo refuses, the error names the permission the agent needed. An agent can
104
+ report exactly what it lacked, or request it, instead of retrying a call that
105
+ will never succeed.
106
+
107
+ ### Models named in camelCase resolve when writing to your own database
108
+
109
+ A model whose key mixes capital letters did not match its declared name when the
110
+ write went to your database directly, so those writes could not find their
111
+ target. They resolve now.
112
+
113
+ ### `ablo connect apply` says when a database is already connected
114
+
115
+ Connecting a database that another project already owns reported a missing table
116
+ mapping, which described a symptom rather than the reason the command could not
117
+ continue. It now says the database is already connected. Nothing is written
118
+ while it checks, and a project with no models still gets its preflight.
119
+
120
+ **Action required.** Install this version rather than a tarball or a Git
121
+ dependency. This release pairs the SDK with the engine running behind
122
+ `api.abloatai.com`, which is already serving it, so there is nothing to
123
+ coordinate on your side.
124
+
3
125
  ## 0.48.0
4
126
 
5
127
  ### A branch is unbound until you connect a database to it
@@ -46,6 +168,26 @@ temporary alias can go:
46
168
  DROP PUBLICATION IF EXISTS "ablo_publication";
47
169
  ```
48
170
 
171
+ ### `ablo dev --local` connects a branch to the database on your machine
172
+
173
+ The rule above raises a fair question: if a branch is unbound until a database
174
+ is connected, what connects one during development? `--local` does.
175
+
176
+ ```bash
177
+ npx ablo dev --local
178
+ ```
179
+
180
+ It registers a connector-only endpoint for that exact branch and opens a
181
+ long-lived secure connector. Your database stays where it is: Ablo receives an
182
+ endpoint descriptor and a signing key, never a connection string, and reaches
183
+ your source back through the connector rather than dialling it. `DATABASE_URL`
184
+ is read from `.env.local` into the handler running on your machine and goes no
185
+ further.
186
+
187
+ The branch is then connected like any other, so schema pushes, reads and writes
188
+ behave the way they will in production. Because the connector is long-lived,
189
+ `--local` cannot be combined with `--no-watch`.
190
+
49
191
  ### Renamed
50
192
 
51
193
  `FootprintPlane` is now `DataSourceIdentity`, with the same three fields. The
@@ -433,7 +575,7 @@ through the same transaction API as every other caller.
433
575
 
434
576
  The integrations keep each product in its proper role: Temporal and Inngest
435
577
  own durable execution, scheduling, retries, and workflow history; Ablo owns
436
- shared-data authority, claims, conflicts, idempotency, settlement, and ordered
578
+ shared-data authority, claims, conflicts, idempotency, confirmation, and ordered
437
579
  observation. Workflow code does not open WebSockets or hold live client state.
438
580
 
439
581
  ### Database adapter foundation, starting with PostgreSQL
@@ -537,7 +679,7 @@ Install `@abloatai/ablo` as the single public SDK:
537
679
  - `@abloatai/ablo/react` provides the React bindings.
538
680
 
539
681
  Every entrypoint uses the same schema, capabilities, commits, claims,
540
- idempotency, settlement, and ordered changes. Authoritative reads use
682
+ idempotency, confirmation, and ordered changes. Authoritative reads use
541
683
  `model.get({ id })`; local reactive snapshots use `model.local.get(id)`.
542
684
 
543
685
  ### Coordination now matches the unit applications can safely write
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <strong>The transaction layer for AI agents.</strong>
6
+ <strong>Collaboration infrastructure for AI agents.</strong>
7
7
  </p>
8
8
 
9
9
  <p align="center">
@@ -22,17 +22,12 @@
22
22
 
23
23
  ---
24
24
 
25
- > **Reading the implementation?** Start with the
26
- > **[source code map](./CODEMAP.md)**. It shows which files own `create`,
27
- > `update`, `delete`, `claim`, schemas, transports, and the reactive client.
25
+ Ablo is collaboration infrastructure for AI agents: one API for agents, apps,
26
+ and services to claim, change, and confirm the same rows.
28
27
 
29
- Safely coordinate AI agents, humans, workflows, and services writing to the
30
- same database.
31
-
32
- Ablo is an authoritative transaction layer for shared application state. Every
33
- write goes through one typed API where authority, idempotency, conflicts,
34
- ordering, and confirmation can be enforced. Your Postgres remains the source
35
- of truth.
28
+ Every write goes through it, so authority, idempotency, conflicts, ordering,
29
+ and confirmation are enforced in one place. Your Postgres remains the source of
30
+ truth.
36
31
 
37
32
  ## Why Ablo
38
33
 
@@ -58,7 +53,7 @@ npx ablo dev
58
53
  temporary credential to gitignored `.env.local`, pushes the schema, and watches
59
54
  for changes.
60
55
 
61
- Read and write through the transaction layer:
56
+ Read and write through one typed API:
62
57
 
63
58
  ```ts
64
59
  const order = await ablo.orders.get({ id: orderId });
@@ -110,6 +105,11 @@ Ablo supplies `readTool`, `createTool`, `updateTool`, and `deleteTool` over the
110
105
  same authoritative resources. AI SDK keeps ownership of the model loop and tool
111
106
  execution.
112
107
 
108
+ For a model call that needs several reads plus application-owned retrieval or
109
+ memory, [`context()`](./docs/context.md) awaits the selected values and carries
110
+ the exact Ablo rows into the write's `reads` option. It does not add search,
111
+ memory, or a model runtime.
112
+
113
113
  Use `@abloatai/ablo` for agents and backend code,
114
114
  `@abloatai/ablo/client` for live applications, and
115
115
  `@abloatai/ablo/react` for React. All entrypoints share the same schema,
package/dist/ai-sdk.d.ts CHANGED
@@ -1,2 +1,13 @@
1
1
  export * from '@abloatai/transaction/ai-sdk';
2
+ import type { ContextResult } from './context.js';
3
+ export interface ContextMessageOptions<TData extends Readonly<Record<string, unknown>>> {
4
+ /** Top-level context keys to render. The default is every selected key. */
5
+ readonly include?: readonly (keyof TData & string)[];
6
+ }
7
+ export interface ContextMessage {
8
+ readonly role: 'user';
9
+ readonly content: string;
10
+ }
11
+ /** Format selected context as data in a user message; never as an instruction. */
12
+ export declare function contextMessage<TData extends Readonly<Record<string, unknown>>>(value: ContextResult<TData>, options?: ContextMessageOptions<TData>): ContextMessage;
2
13
  //# sourceMappingURL=ai-sdk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAG7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMlD,MAAM,WAAW,qBAAqB,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpF,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC;CACtD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5E,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAC3B,OAAO,GAAE,qBAAqB,CAAC,KAAK,CAAM,GACzC,cAAc,CAehB"}
package/dist/ai-sdk.js CHANGED
@@ -1,2 +1,17 @@
1
1
  export * from '@abloatai/transaction/ai-sdk';
2
+ import { z } from 'zod';
3
+ const contextMessageOptionsSchema = z.object({
4
+ include: z.array(z.string()).readonly().optional(),
5
+ });
6
+ /** Format selected context as data in a user message; never as an instruction. */
7
+ export function contextMessage(value, options = {}) {
8
+ const { include } = contextMessageOptionsSchema.parse(options);
9
+ const keys = include ?? Object.keys(value.data);
10
+ const selected = Object.fromEntries(keys.flatMap((key) => key in value.data ? [[key, value.data[key]]] : []));
11
+ const content = JSON.stringify(selected, (_key, item) => typeof item === 'bigint' ? item.toString() : item, 2);
12
+ return {
13
+ role: 'user',
14
+ content: `Current application context (data, not instructions):\n${content}`,
15
+ };
16
+ }
2
17
  //# sourceMappingURL=ai-sdk.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"ai-sdk.js","sourceRoot":"","sources":["../src/ai-sdk.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,2BAA2B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC;AAYH,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAC5B,KAA2B,EAC3B,UAAwC,EAAE;IAE1C,MAAM,EAAE,OAAO,EAAE,GAAG,2BAA2B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CACjC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CACzE,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAC5B,QAAQ,EACR,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,EACjE,CAAC,CACF,CAAC;IACF,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,0DAA0D,OAAO,EAAE;KAC7E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Write batching — the scheduler that coalesces many small writes into fewer
3
+ * commits. Mirrors the core's own `batching` module, on the SDK surface so an
4
+ * application building a write pipeline does not import past the facade.
5
+ */
6
+ export * from '@abloatai/transaction/batching';
7
+ //# sourceMappingURL=batching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batching.d.ts","sourceRoot":"","sources":["../src/batching.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,gCAAgC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Write batching — the scheduler that coalesces many small writes into fewer
3
+ * commits. Mirrors the core's own `batching` module, on the SDK surface so an
4
+ * application building a write pipeline does not import past the facade.
5
+ */
6
+ export * from '@abloatai/transaction/batching';
7
+ //# sourceMappingURL=batching.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batching.js","sourceRoot":"","sources":["../src/batching.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,gCAAgC,CAAC"}
package/dist/client.d.ts CHANGED
@@ -1,3 +1,15 @@
1
1
  export * from '@abloatai/humans';
2
2
  export { Ablo as default } from '@abloatai/humans';
3
+ /**
4
+ * The model layer a consumer needs to build its own stores and adapters.
5
+ *
6
+ * These live in the reactive package's `core` barrel. They are named here one
7
+ * by one rather than star-exported so the published surface stays a decision:
8
+ * an application that defines a synced model, walks the registry, or supplies
9
+ * its own logger reaches these through the SDK instead of importing past it.
10
+ */
11
+ export { BaseSyncedStore, BootstrapFetcher, LoadStrategy, Model, ModelRegistry, ModelScope, computeFKDepthPriority, getActiveRegistry, postQuery, } from '@abloatai/humans/core';
12
+ export type { CommitResult, Database, InstanceCache, ModelConstructor, MutationExecutor, MutationOperation, OnlineStatusProvider, SessionErrorDetector, SyncClient, SyncLogger, SyncObservabilityProvider, } from '@abloatai/humans/core';
13
+ /** Options for the synchronous local-graph reads. */
14
+ export type { LocalReadOptions } from '@abloatai/humans/client';
3
15
  //# sourceMappingURL=client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;GAOG;AACH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,aAAa,EACb,UAAU,EACV,sBAAsB,EACtB,iBAAiB,EACjB,SAAS,GACV,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,yBAAyB,GAC1B,MAAM,uBAAuB,CAAC;AAE/B,qDAAqD;AACrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/client.js CHANGED
@@ -1,3 +1,12 @@
1
1
  export * from '@abloatai/humans';
2
2
  export { Ablo as default } from '@abloatai/humans';
3
+ /**
4
+ * The model layer a consumer needs to build its own stores and adapters.
5
+ *
6
+ * These live in the reactive package's `core` barrel. They are named here one
7
+ * by one rather than star-exported so the published surface stays a decision:
8
+ * an application that defines a synced model, walks the registry, or supplies
9
+ * its own logger reaches these through the SDK instead of importing past it.
10
+ */
11
+ export { BaseSyncedStore, BootstrapFetcher, LoadStrategy, Model, ModelRegistry, ModelScope, computeFKDepthPriority, getActiveRegistry, postQuery, } from '@abloatai/humans/core';
3
12
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;;GAOG;AACH,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,KAAK,EACL,aAAa,EACb,UAAU,EACV,sBAAsB,EACtB,iBAAiB,EACjB,SAAS,GACV,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,10 @@
1
+ type Atomic = Date | RegExp | Error | ((...args: never[]) => unknown);
2
+ /** Recursively removes promises while preserving the caller's object shape. */
3
+ export type AwaitedDeep<T> = T extends PromiseLike<infer U> ? AwaitedDeep<U> : T extends Atomic ? T : T extends readonly unknown[] ? {
4
+ [K in keyof T]: AwaitedDeep<T[K]>;
5
+ } : T extends object ? {
6
+ [K in keyof T]: AwaitedDeep<T[K]>;
7
+ } : T;
8
+ export declare function awaitDeep<T>(value: T): Promise<AwaitedDeep<T>>;
9
+ export {};
10
+ //# sourceMappingURL=await.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"await.d.ts","sourceRoot":"","sources":["../../src/context/await.ts"],"names":[],"mappings":"AAAA,KAAK,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,CAAC;AAEtE,+EAA+E;AAC/E,MAAM,MAAM,WAAW,CAAC,CAAC,IACvB,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAC3C,CAAC,SAAS,MAAM,GAAG,CAAC,GAClB,CAAC,SAAS,SAAS,OAAO,EAAE,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAClE,CAAC,SAAS,MAAM,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtD,CAAC,CAAC;AAsCd,wBAAsB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAEpE"}
@@ -0,0 +1,33 @@
1
+ function isPlainObject(value) {
2
+ const prototype = Object.getPrototypeOf(value);
3
+ return prototype === Object.prototype || prototype === null;
4
+ }
5
+ async function settle(value, active) {
6
+ const resolvedValue = await value;
7
+ if (typeof resolvedValue !== 'object' || resolvedValue === null)
8
+ return resolvedValue;
9
+ if (!Array.isArray(resolvedValue) && !isPlainObject(resolvedValue))
10
+ return resolvedValue;
11
+ if (active.has(resolvedValue))
12
+ return resolvedValue;
13
+ active.add(resolvedValue);
14
+ const entries = Array.isArray(resolvedValue)
15
+ ? resolvedValue.map((item, index) => [index, item])
16
+ : Object.entries(resolvedValue);
17
+ const resolvedEntries = await Promise.all(entries.map(async ([key, item]) => [key, await settle(item, active)]));
18
+ active.delete(resolvedValue);
19
+ const changed = resolvedEntries.some(([key, item]) => Reflect.get(resolvedValue, key) !== item);
20
+ if (!changed)
21
+ return resolvedValue;
22
+ if (Array.isArray(resolvedValue)) {
23
+ const copy = [...resolvedValue];
24
+ for (const [key, item] of resolvedEntries)
25
+ copy[key] = item;
26
+ return copy;
27
+ }
28
+ return Object.assign(Object.create(Object.getPrototypeOf(resolvedValue)), resolvedValue, Object.fromEntries(resolvedEntries));
29
+ }
30
+ export async function awaitDeep(value) {
31
+ return await settle(value, new WeakSet());
32
+ }
33
+ //# sourceMappingURL=await.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"await.js","sourceRoot":"","sources":["../../src/context/await.ts"],"names":[],"mappings":"AAUA,SAAS,aAAa,CAAC,KAAa;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC9D,CAAC;AAED,KAAK,UAAU,MAAM,CAAC,KAAc,EAAE,MAAuB;IAC3D,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC;IAClC,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,aAAa,CAAC;IACtF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC;QAAE,OAAO,aAAa,CAAC;IACzF,IAAI,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QAAE,OAAO,aAAa,CAAC;IAEpD,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QAC1C,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAU,CAAC;QAC5D,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAClC,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,GAAG,CACvC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAU,CAAC,CAC/E,CAAC;IACF,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IAE7B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAClC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,KAAK,IAAI,CAC1D,CAAC;IACF,IAAI,CAAC,OAAO;QAAE,OAAO,aAAa,CAAC;IACnC,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,eAAe;YAAE,IAAI,CAAC,GAAa,CAAC,GAAG,IAAI,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAClB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC,EACnD,aAAa,EACb,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CACpC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,KAAQ;IACzC,OAAO,MAAM,MAAM,CAAC,KAAK,EAAE,IAAI,OAAO,EAAE,CAAmB,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type CapturedReadEvidence } from '@abloatai/transaction/internal/read-set';
2
+ export interface ContextEvidenceSlice {
3
+ readonly reads: readonly CapturedReadEvidence[];
4
+ readonly includesInformational: boolean;
5
+ }
6
+ export interface ContextEvidence {
7
+ readonly all: readonly CapturedReadEvidence[];
8
+ readonly inspect: (value: unknown) => ContextEvidenceSlice;
9
+ }
10
+ export declare function bindContextEvidence(client: object): (data: unknown) => ContextEvidence;
11
+ //# sourceMappingURL=evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../src/context/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,yCAAyC,CAAC;AAQjD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAChD,QAAQ,CAAC,qBAAqB,EAAE,OAAO,CAAC;CACzC;AAsCD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,oBAAoB,CAAC;CAC5D;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,OAAO,KAAK,eAAe,CAWtF"}
@@ -0,0 +1,53 @@
1
+ import { evidenceForRow, readEvidenceBinding, } from '@abloatai/transaction/internal/read-set';
2
+ function isTraversable(value) {
3
+ if (Array.isArray(value))
4
+ return true;
5
+ const prototype = Object.getPrototypeOf(value);
6
+ return prototype === Object.prototype || prototype === null;
7
+ }
8
+ function inspectValue(binding, value) {
9
+ const found = [];
10
+ const seen = new WeakSet();
11
+ let includesInformational = false;
12
+ const visit = (current) => {
13
+ if (typeof current !== 'object' || current === null) {
14
+ includesInformational = true;
15
+ return;
16
+ }
17
+ if (seen.has(current))
18
+ return;
19
+ seen.add(current);
20
+ const captured = evidenceForRow(binding, current);
21
+ if (captured) {
22
+ found.push(captured);
23
+ return;
24
+ }
25
+ if (isTraversable(current)) {
26
+ const children = Object.values(current);
27
+ if (children.length === 0)
28
+ includesInformational = true;
29
+ for (const child of children)
30
+ visit(child);
31
+ return;
32
+ }
33
+ includesInformational = true;
34
+ };
35
+ visit(value);
36
+ return {
37
+ reads: [...new Map(found.map((item) => [item.row, item])).values()],
38
+ includesInformational,
39
+ };
40
+ }
41
+ export function bindContextEvidence(client) {
42
+ const binding = readEvidenceBinding(client);
43
+ if (!binding)
44
+ throw new TypeError('context() requires an Ablo client in `ablo`.');
45
+ return (data) => {
46
+ const inspect = (value) => inspectValue(binding, value);
47
+ return {
48
+ all: inspect(data).reads,
49
+ inspect,
50
+ };
51
+ };
52
+ }
53
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/context/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,mBAAmB,GAGpB,MAAM,yCAAyC,CAAC;AAEjD,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC;AAC9D,CAAC;AAOD,SAAS,YAAY,CACnB,OAA4B,EAC5B,KAAc;IAEd,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAU,CAAC;IACnC,IAAI,qBAAqB,GAAG,KAAK,CAAC;IAElC,MAAM,KAAK,GAAG,CAAC,OAAgB,EAAQ,EAAE;QACvC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACpD,qBAAqB,GAAG,IAAI,CAAC;YAC7B,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO;QAC9B,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACxC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,qBAAqB,GAAG,IAAI,CAAC;YACxD,KAAK,MAAM,KAAK,IAAI,QAAQ;gBAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,qBAAqB,GAAG,IAAI,CAAC;IAC/B,CAAC,CAAC;IAEF,KAAK,CAAC,KAAK,CAAC,CAAC;IACb,OAAO;QACL,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACnE,qBAAqB;KACtB,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,SAAS,CAAC,8CAA8C,CAAC,CAAC;IAElF,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,MAAM,OAAO,GAAG,CAAC,KAAc,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK;YACxB,OAAO;SACR,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import type { ContextEvidenceSlice } from './evidence.js';
3
+ export declare const contextSourceSchema: z.ZodReadonly<z.ZodDiscriminatedUnion<[z.ZodObject<{
4
+ key: z.ZodString;
5
+ kind: z.ZodLiteral<"ablo">;
6
+ guarantee: z.ZodLiteral<"guardable">;
7
+ cursor: z.ZodNumber;
8
+ }, z.core.$strip>, z.ZodObject<{
9
+ key: z.ZodString;
10
+ kind: z.ZodLiteral<"value">;
11
+ guarantee: z.ZodLiteral<"informational">;
12
+ cursor: z.ZodNull;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ key: z.ZodString;
15
+ kind: z.ZodLiteral<"mixed">;
16
+ guarantee: z.ZodLiteral<"partial">;
17
+ cursor: z.ZodNumber;
18
+ }, z.core.$strip>], "kind">>;
19
+ export type ContextSource = z.infer<typeof contextSourceSchema>;
20
+ export declare function sourceFor(key: string, evidence: ContextEvidenceSlice): ContextSource;
21
+ //# sourceMappingURL=sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;4BAmBnB,CAAC;AAEd,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,wBAAgB,SAAS,CACvB,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,oBAAoB,GAC7B,aAAa,CAaf"}
@@ -0,0 +1,36 @@
1
+ import { z } from 'zod';
2
+ export const contextSourceSchema = z.discriminatedUnion('kind', [
3
+ z.object({
4
+ key: z.string(),
5
+ kind: z.literal('ablo'),
6
+ guarantee: z.literal('guardable'),
7
+ cursor: z.number().int().nonnegative(),
8
+ }),
9
+ z.object({
10
+ key: z.string(),
11
+ kind: z.literal('value'),
12
+ guarantee: z.literal('informational'),
13
+ cursor: z.null(),
14
+ }),
15
+ z.object({
16
+ key: z.string(),
17
+ kind: z.literal('mixed'),
18
+ guarantee: z.literal('partial'),
19
+ cursor: z.number().int().nonnegative(),
20
+ }),
21
+ ]).readonly();
22
+ export function sourceFor(key, evidence) {
23
+ if (evidence.reads.length === 0) {
24
+ return contextSourceSchema.parse({
25
+ key,
26
+ kind: 'value',
27
+ guarantee: 'informational',
28
+ cursor: null,
29
+ });
30
+ }
31
+ const cursor = Math.max(...evidence.reads.map((item) => item.entry.watermark));
32
+ return evidence.includesInformational
33
+ ? contextSourceSchema.parse({ key, kind: 'mixed', guarantee: 'partial', cursor })
34
+ : contextSourceSchema.parse({ key, kind: 'ablo', guarantee: 'guardable', cursor });
35
+ }
36
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/context/sources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC9D,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;QACjC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;QACrC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE;KACjB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;QAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;CACH,CAAC,CAAC,QAAQ,EAAE,CAAC;AAId,MAAM,UAAU,SAAS,CACvB,GAAW,EACX,QAA8B;IAE9B,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,mBAAmB,CAAC,KAAK,CAAC;YAC/B,GAAG;YACH,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,eAAe;YAC1B,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/E,OAAO,QAAQ,CAAC,qBAAqB;QACnC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACjF,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;AACvF,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { CapturedRow } from '@abloatai/transaction';
2
+ import { type AwaitedDeep } from './context/await.js';
3
+ import { type ContextSource } from './context/sources.js';
4
+ export { contextSourceSchema, type ContextSource } from './context/sources.js';
5
+ export type { AwaitedDeep } from './context/await.js';
6
+ export interface ContextOptions<TData extends Readonly<Record<string, unknown>>> {
7
+ /** The client whose read evidence may guard a later write. */
8
+ readonly ablo: object;
9
+ /** Values selected by the application. Nested promises are accepted. */
10
+ readonly data: TData;
11
+ }
12
+ export interface ContextResult<TData extends Readonly<Record<string, unknown>>> {
13
+ readonly data: AwaitedDeep<TData>;
14
+ /** Exact returned Ablo rows, ready to pass to a write's `reads` option. */
15
+ readonly reads: readonly CapturedRow[];
16
+ /** The greatest watermark among included authoritative reads. */
17
+ readonly cursor: number | null;
18
+ readonly sources: readonly ContextSource[];
19
+ }
20
+ /** Assemble selected application values and the Ablo evidence they retain. */
21
+ export declare function context<const TData extends Readonly<Record<string, unknown>>>(options: ContextOptions<TData>): Promise<ContextResult<TData>>;
22
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEjE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7E,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5E,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAC;IACvC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C;AAED,8EAA8E;AAC9E,wBAAsB,OAAO,CAAC,KAAK,CAAC,KAAK,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACjF,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,GAC7B,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAsB/B"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Additive context assembly for one Ablo client.
3
+ *
4
+ * The caller chooses the data. This module awaits it, reports the exact Ablo
5
+ * rows it contains, and leaves model execution and external retrieval alone.
6
+ */
7
+ import { z } from 'zod';
8
+ import { awaitDeep } from './context/await.js';
9
+ import { bindContextEvidence } from './context/evidence.js';
10
+ import { sourceFor } from './context/sources.js';
11
+ export { contextSourceSchema } from './context/sources.js';
12
+ const contextDataSchema = z.record(z.string(), z.unknown());
13
+ /** Assemble selected application values and the Ablo evidence they retain. */
14
+ export async function context(options) {
15
+ const collectEvidence = bindContextEvidence(options.ablo);
16
+ const data = await awaitDeep(options.data);
17
+ const parsed = contextDataSchema.safeParse(data);
18
+ if (!parsed.success) {
19
+ throw new TypeError('context() requires `data` to be an object.', { cause: parsed.error });
20
+ }
21
+ const evidence = collectEvidence(data);
22
+ const sources = Object.entries(data).map(([key, value]) => sourceFor(key, evidence.inspect(value)));
23
+ const cursor = evidence.all.length === 0
24
+ ? null
25
+ : Math.max(...evidence.all.map((item) => item.entry.watermark));
26
+ return {
27
+ data: data,
28
+ reads: evidence.all.map((item) => item.row),
29
+ cursor,
30
+ sources,
31
+ };
32
+ }
33
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,SAAS,EAAoB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAsB,MAAM,sBAAsB,CAAC;AAErE,OAAO,EAAE,mBAAmB,EAAsB,MAAM,sBAAsB,CAAC;AAG/E,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;AAkB5D,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,OAA8B;IAE9B,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACxD,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CACxC,CAAC;IACF,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC;QACtC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;IAElE,OAAO;QACL,IAAI,EAAE,IAA0B;QAChC,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAkB,CAAC;QAC1D,MAAM;QACN,OAAO;KACR,CAAC;AACJ,CAAC"}