@abloatai/ablo 0.55.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/docs/customer-organizations.md +117 -155
- package/docs/data-sources.md +10 -12
- package/package.json +5 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.56.0
|
|
4
|
+
|
|
5
|
+
### One customer no longer sees another's live work
|
|
6
|
+
|
|
7
|
+
A platform's customers are rows in its own schema, so they share one
|
|
8
|
+
organization and one plane. The claim listing and the presence read were scoped
|
|
9
|
+
to exactly that pair and nothing finer. One customer could therefore see which
|
|
10
|
+
rows another had claimed, who held them, what the work was called, and who was
|
|
11
|
+
online. Row contents were never exposed; everything around them was.
|
|
12
|
+
|
|
13
|
+
Both reads now apply the same cut the delivery path already applies, taken from
|
|
14
|
+
the groups each side already carries. A customer sees the coordination for the
|
|
15
|
+
rows it can see, and nothing else. If you serve many customers from one
|
|
16
|
+
organization, this closes the gap without any change on your side.
|
|
17
|
+
|
|
18
|
+
### A client converges on the head it was measured against
|
|
19
|
+
|
|
20
|
+
Catch-up measured the plane head, paged the log under the client's own scope, and
|
|
21
|
+
then set the cursor to the last row that scope happened to contain. On a plane
|
|
22
|
+
carrying traffic the client cannot see, that row sits below the head. Where the
|
|
23
|
+
scope held nothing at all, the cursor never moved.
|
|
24
|
+
|
|
25
|
+
The client half was the mirror image: it reconciled in one direction only and
|
|
26
|
+
could not adopt a head above its own. Together those left a client permanently
|
|
27
|
+
behind, and the catch-up poll turned that into standing load, taking the plane's
|
|
28
|
+
advisory lock every thirty seconds to find the same gap and serve the same
|
|
29
|
+
nothing. The head reads a global sequence, so on any deployment with more than
|
|
30
|
+
one active writer plane this was every client rather than an edge case.
|
|
31
|
+
|
|
32
|
+
The server now advances to the head it measured, and the client adopts a head
|
|
33
|
+
above its cursor when the response carries no deltas, because an empty response
|
|
34
|
+
is proof rather than a hint.
|
|
35
|
+
|
|
36
|
+
### A replicated array column arrives as an array
|
|
37
|
+
|
|
38
|
+
Every array column read through replication arrived one level too deep: `{a,b}`
|
|
39
|
+
as `[["a","b"]]`, and `{}` as `[[]]`. The driver's array parsers expect the
|
|
40
|
+
literal without its leading brace, and given the whole literal they read that
|
|
41
|
+
brace as the start of a nested array. The control plane refused such a value
|
|
42
|
+
outright; a `text[]` column in your own database would have carried it into the
|
|
43
|
+
log silently.
|
|
44
|
+
|
|
45
|
+
### `ablo doctor` separates two different failures
|
|
46
|
+
|
|
47
|
+
A plane where nothing routed at all and a plane where some changes did not have
|
|
48
|
+
different causes, so they no longer read the same:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
✗ delivery no change reached anyone (41 in the last hour)
|
|
52
|
+
→ run `ablo check`. When nothing routes, the tenancy value is usually missing for the whole plane rather than for particular rows.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `LogPosition` is the one name for a position in the log
|
|
56
|
+
|
|
57
|
+
`DeltaPosition`, `deltaPositionSchema`, `ReadSetWatermark`, and
|
|
58
|
+
`readSetWatermarkSchema` still resolve to it and are removed in 0.57.0. Where a
|
|
59
|
+
position needs an owner, the owner goes in the field name rather than into a
|
|
60
|
+
second type.
|
|
61
|
+
|
|
62
|
+
`ABLO_DOCS_BASE_URL` and `ABLO_SITE_BASE_URL` are exported for tools that link
|
|
63
|
+
back to the documentation.
|
|
64
|
+
|
|
65
|
+
### What an organization is, and what your customers are
|
|
66
|
+
|
|
67
|
+
The customer-organizations guide is rewritten around the distinction it kept
|
|
68
|
+
blurring. An organization is a team account: people join it with their own
|
|
69
|
+
logins, and share what it owns and is billed for. Nobody invites their customers
|
|
70
|
+
into that.
|
|
71
|
+
|
|
72
|
+
So a platform's customers are not organizations, and they are not projects
|
|
73
|
+
either, since a project is bound one to one to a database schema and an account
|
|
74
|
+
with four applications could no longer say which of the four a customer belonged
|
|
75
|
+
to. They are rows in the platform's own schema, reached by the sync groups on the
|
|
76
|
+
session: the account is ambient and derived from the key, the customer is a plain
|
|
77
|
+
row, and the session is minted against one of them.
|
|
78
|
+
|
|
3
79
|
## 0.55.0
|
|
4
80
|
|
|
5
81
|
### `ablo doctor` says whether the writes reached anyone
|
|
@@ -1,71 +1,49 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Serving Many Customers
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> One account, one schema, and a session scoped to the customer whose data it may read.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
customer keep a hard organization boundary while all of them use the schema
|
|
8
|
-
pushed once by the owning project.
|
|
9
|
-
|
|
10
|
-
## Choose the customer boundary first
|
|
11
|
-
|
|
12
|
-
| Customer model | Isolation guarantee | Choose it when |
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| One Ablo organization with customer scope roots | Whatever read boundary every model declares in `policy` | Cross-customer reads are intentional, or every model is explicitly and continuously policy-partitioned |
|
|
15
|
-
| One Ablo organization per customer | Structural organization filtering and RLS on every row | Customers must stay isolated even when a model has no customer-specific policy |
|
|
16
|
-
|
|
17
|
-
Sync-groups decide which changes are delivered. They do not, by themselves,
|
|
18
|
-
authorize HTTP reads. If you cannot audit matching policies across every model,
|
|
19
|
-
use one Ablo organization per customer.
|
|
20
|
-
|
|
21
|
-
The rest of this guide uses organization-per-customer: one trusted backend
|
|
22
|
-
holds one dedicated mint key, every customer has an `organizationId`, and each
|
|
23
|
-
user session names that customer organization.
|
|
24
|
-
|
|
25
|
-
## What you need
|
|
26
|
-
|
|
27
|
-
- An owning project containing the schema every customer uses.
|
|
28
|
-
- The schema pushed to that project's production root.
|
|
29
|
-
- A server-side `sk_` carrying only `organization:act-as`.
|
|
30
|
-
- A customer `organizationId` resolved from your authenticated application
|
|
31
|
-
membership, never accepted unchecked from the browser.
|
|
32
|
-
- A model-by-model `can` grant for the UI being opened.
|
|
33
|
-
|
|
34
|
-
The cross-organization key is a minting credential, not a tenant-data
|
|
35
|
-
credential. Keep it in a secret manager and expose only your own authenticated
|
|
36
|
-
session endpoint.
|
|
37
|
-
|
|
38
|
-
## Mint on your backend
|
|
39
|
-
|
|
40
|
-
Create the Ablo client once in server-only code:
|
|
5
|
+
Your customers live in your database, in a table you already have. Ablo reaches
|
|
6
|
+
one of them by the groups a session is minted with.
|
|
41
7
|
|
|
42
8
|
```ts
|
|
43
|
-
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
export const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
9
|
+
// 1. src/ablo/schema.ts — your customer table is a scope root.
|
|
10
|
+
import { defineSchema, identityRole, relation, model, z } from '@abloatai/ablo/schema';
|
|
11
|
+
|
|
12
|
+
export const schema = defineSchema(
|
|
13
|
+
{
|
|
14
|
+
// Its rows form the group `customer:<id>`; the kind comes from `groups.root`.
|
|
15
|
+
customers: model(
|
|
16
|
+
{ name: z.string() },
|
|
17
|
+
{ groups: { root: 'customer' } },
|
|
18
|
+
),
|
|
19
|
+
// A child inherits its customer's group through the `parent` edge.
|
|
20
|
+
decks: model(
|
|
21
|
+
{ customerId: z.string(), title: z.string() },
|
|
22
|
+
{ relations: { customer: relation.belongsTo('customers', 'customerId', { parent: true }) } },
|
|
23
|
+
),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
identityRoles: [
|
|
27
|
+
identityRole({ kind: 'org', source: 'organizationId' }),
|
|
28
|
+
identityRole({ kind: 'user', source: 'userId' }),
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
);
|
|
50
32
|
```
|
|
51
33
|
|
|
52
|
-
After your application authenticates the user, resolve the customer from that
|
|
53
|
-
trusted membership and mint the session:
|
|
54
|
-
|
|
55
34
|
```ts
|
|
35
|
+
// 2. app/api/ablo-session/route.ts — mint for one customer, on your backend.
|
|
36
|
+
import { syncGroup } from '@abloatai/ablo/schema';
|
|
56
37
|
import { credentialEndpointSuccessSchema } from '@abloatai/ablo/auth';
|
|
57
|
-
import {
|
|
38
|
+
import { ablo } from '@/ablo/server';
|
|
58
39
|
|
|
59
40
|
export async function POST() {
|
|
60
|
-
const member = await
|
|
41
|
+
const member = await requireSignedInMember();
|
|
61
42
|
|
|
62
|
-
const session = await
|
|
43
|
+
const session = await ablo.sessions.create({
|
|
63
44
|
user: { id: member.userId },
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
projects: ['read'],
|
|
67
|
-
records: ['read', 'create', 'update'],
|
|
68
|
-
},
|
|
45
|
+
can: { customers: ['read'], decks: ['read', 'create', 'update'] },
|
|
46
|
+
syncGroups: [syncGroup('customer', member.customerId)],
|
|
69
47
|
});
|
|
70
48
|
|
|
71
49
|
return Response.json(
|
|
@@ -79,137 +57,121 @@ export async function POST() {
|
|
|
79
57
|
}
|
|
80
58
|
```
|
|
81
59
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
you just authenticated.
|
|
60
|
+
That is the whole integration. The rest of this page is why each line is where
|
|
61
|
+
it is.
|
|
85
62
|
|
|
86
|
-
##
|
|
63
|
+
## What each layer is
|
|
87
64
|
|
|
88
|
-
|
|
89
|
-
|
|
65
|
+
Four things carry a name in this arrangement, and mixing two of them up is the
|
|
66
|
+
one mistake worth spending a page to prevent.
|
|
90
67
|
|
|
91
|
-
|
|
92
|
-
|
|
68
|
+
| Layer | What it is | Where it lives |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| Your account | The organization you signed up with. Colleagues join it with their own logins and share one bill. | Ablo |
|
|
71
|
+
| Your application | A project. One per app you run, bound to one schema in your database. | Ablo |
|
|
72
|
+
| Your customer | A row in your own table, with your own id on it. | Your database |
|
|
73
|
+
| One person's session | An `ek_` your backend mints, cut to one customer's group. | Minted per sign-in |
|
|
93
74
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
75
|
+
Your customers sit in the third row. They are not accounts, because an account
|
|
76
|
+
is something you invite colleagues into. They are not projects, because a
|
|
77
|
+
project binds to a Postgres schema and you run one application, not one per
|
|
78
|
+
customer.
|
|
97
79
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
authEndpoint: '/api/ablo-session',
|
|
101
|
-
});
|
|
80
|
+
Your `sk_` already carries your account, so a session never names it. What the
|
|
81
|
+
session adds is which customer the person in front of it may read.
|
|
102
82
|
|
|
103
|
-
|
|
104
|
-
return <AbloProvider client={ablo}>{children}</AbloProvider>;
|
|
105
|
-
}
|
|
106
|
-
```
|
|
83
|
+
## Where the boundary is enforced
|
|
107
84
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
server failures are transient and must not sign the user out.
|
|
85
|
+
Two mechanisms do different jobs, and it is worth knowing which is which before
|
|
86
|
+
you rely on either.
|
|
111
87
|
|
|
112
|
-
|
|
88
|
+
**Your account is the tenant boundary.** Every row Ablo stores carries your
|
|
89
|
+
organization and project, and row-level security compares both against the
|
|
90
|
+
credential on every read and every write. A client cannot reach past it by
|
|
91
|
+
asking, because the values come from the key rather than the request.
|
|
113
92
|
|
|
114
|
-
|
|
115
|
-
|
|
93
|
+
**Sync groups are the cut inside your account.** They decide which of your own
|
|
94
|
+
rows a session is delivered and which it may read back over HTTP. This is the
|
|
95
|
+
boundary between one of your customers and the next, and it is the one your
|
|
96
|
+
schema declares.
|
|
116
97
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
session can -> allowed model operations
|
|
121
|
-
model policy -> allowed reads inside that organization
|
|
122
|
-
```
|
|
98
|
+
Reads go through it. `list` and `get` are cut to the session's groups, live
|
|
99
|
+
delivery is cut to the same set, and the initial load is the intersection of
|
|
100
|
+
what the session asked for with what it was minted with.
|
|
123
101
|
|
|
124
|
-
|
|
125
|
-
|
|
102
|
+
Coordination reads are cut more coarsely today. Listing claims and reading
|
|
103
|
+
presence are scoped to your account and plane rather than to the session's
|
|
104
|
+
groups, so one customer's session can see that a row is claimed and who is
|
|
105
|
+
present, though never the row's contents. Treat row ids and participant ids on
|
|
106
|
+
those two surfaces as visible across your customers until this page says
|
|
107
|
+
otherwise.
|
|
108
|
+
|
|
109
|
+
## Naming a group
|
|
110
|
+
|
|
111
|
+
Build a group with the `syncGroup(kind, id)` helper rather than a string. The
|
|
112
|
+
kind is the one you declared in `groups.root`, and the id is your own
|
|
113
|
+
identifier for the customer.
|
|
126
114
|
|
|
127
115
|
```ts
|
|
128
|
-
|
|
129
|
-
user: { id: member.userId },
|
|
130
|
-
organizationId: member.abloOrganizationId,
|
|
131
|
-
schemaProject: {
|
|
132
|
-
organizationId: schemaOwnerOrganizationId,
|
|
133
|
-
projectId: migratingSchemaProjectId,
|
|
134
|
-
},
|
|
135
|
-
can: { records: ['read', 'update'] },
|
|
136
|
-
});
|
|
116
|
+
syncGroups: [syncGroup('customer', member.customerId)]
|
|
137
117
|
```
|
|
138
118
|
|
|
139
|
-
|
|
119
|
+
Resolve `member.customerId` from the membership you just authenticated on the
|
|
120
|
+
server. A signed-in person can put any value in a request body, and the session
|
|
121
|
+
you mint is what decides what they can read.
|
|
140
122
|
|
|
141
|
-
##
|
|
123
|
+
## When a customer really is an account
|
|
142
124
|
|
|
143
|
-
|
|
125
|
+
There is one shape where each of your customers should be its own Ablo
|
|
126
|
+
organization: when each is a separate paying business that signs in to Ablo
|
|
127
|
+
itself, holds its own subscription, and invites its own developers. That is what
|
|
128
|
+
an identity provider looks like, and it is what the `organization:act-as` scope
|
|
129
|
+
on a secret key exists for.
|
|
144
130
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
2. Connect that organization's production data source if it owns a separate
|
|
148
|
-
database.
|
|
149
|
-
3. Keep the shared schema in the owning project; do not copy it into every
|
|
150
|
-
customer organization.
|
|
151
|
-
4. Mint a test user session through the same backend route production uses.
|
|
152
|
-
5. Verify one known read and write in the customer organization before enabling
|
|
153
|
-
the integration.
|
|
131
|
+
It is rare, and it is not what a platform serving customers from one product
|
|
132
|
+
looks like. If your customers never see Ablo, they belong in your schema.
|
|
154
133
|
|
|
155
|
-
|
|
156
|
-
immediate cutoff is required, and retire the customer's data-source access
|
|
157
|
-
through the control-plane process you use for provisioning.
|
|
134
|
+
## Onboarding a customer
|
|
158
135
|
|
|
159
|
-
|
|
136
|
+
Insert the row. There is nothing to register with Ablo, because the group is
|
|
137
|
+
derived from the row's id, and the first session minted against it is delivered
|
|
138
|
+
its data.
|
|
160
139
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
data authority.
|
|
164
|
-
- Resolve `organizationId` from authenticated membership server-side.
|
|
165
|
-
- Keep `can` to the smallest model/verb set the UI needs.
|
|
166
|
-
- Use organization-per-customer when a missing model policy must not expose
|
|
167
|
-
another customer's rows.
|
|
168
|
-
- Rotate the cross-organization key on a schedule and after personnel or
|
|
169
|
-
infrastructure changes.
|
|
170
|
-
- Record target organization, user, session id, and request id without logging
|
|
171
|
-
plaintext credentials.
|
|
140
|
+
Add a project only when you add an application. `npx ablo projects create` takes
|
|
141
|
+
a management credential from `ablo login`, and one project holds one schema.
|
|
172
142
|
|
|
173
143
|
## Troubleshooting
|
|
174
144
|
|
|
175
|
-
###
|
|
145
|
+
### A session reads nothing
|
|
176
146
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
backend environment to confirm which project and branch the configured key
|
|
181
|
-
actually belongs to; the command never prints the full secret.
|
|
147
|
+
Check the groups the session was minted with against the kind in `groups.root`.
|
|
148
|
+
A group whose kind is not declared matches nothing, which reads as an empty
|
|
149
|
+
database rather than an error.
|
|
182
150
|
|
|
183
|
-
###
|
|
151
|
+
### A session reads another customer's rows
|
|
184
152
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
supplied `schemaProject`, remove it unless you intentionally override the
|
|
189
|
-
owning-key default.
|
|
153
|
+
Check that the model declares a `parent` edge up to the scope root. A model with
|
|
154
|
+
no group of its own and no parent belongs to no group, so a group cut does not
|
|
155
|
+
narrow it.
|
|
190
156
|
|
|
191
|
-
### The mint
|
|
157
|
+
### The mint is refused
|
|
192
158
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
159
|
+
Naming `organizationId` reaches into a different account and takes
|
|
160
|
+
`organization:act-as`. A platform serving its own customers names groups
|
|
161
|
+
instead, and its key needs no scope at all.
|
|
196
162
|
|
|
197
|
-
|
|
163
|
+
## See it yourself
|
|
198
164
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
### Realtime looks isolated but an HTTP read is too broad
|
|
165
|
+
```
|
|
166
|
+
npx ablo whoami --json
|
|
167
|
+
```
|
|
204
168
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
boundary must hold structurally across every model.
|
|
169
|
+
The `syncGroups` it reports are the cut the engine will apply. If a customer's
|
|
170
|
+
group is missing there, no read will show its rows.
|
|
208
171
|
|
|
209
172
|
## Related guides
|
|
210
173
|
|
|
211
|
-
- [
|
|
212
|
-
- [
|
|
213
|
-
- [
|
|
214
|
-
- [
|
|
215
|
-
- [Deployment](./deployment.md) — production schema and database rollout order.
|
|
174
|
+
- [Identity & Sync Groups](/identity) — how groups are declared and resolved.
|
|
175
|
+
- [Sessions](/sessions) — session lifetime, refresh, and revocation.
|
|
176
|
+
- [API Keys](/api-keys) — credential classes and scopes.
|
|
177
|
+
- [Projects](/projects) — one project per application.
|
package/docs/data-sources.md
CHANGED
|
@@ -355,13 +355,11 @@ Because Ablo checks from its own network, a database your own machine can't reac
|
|
|
355
355
|
IPv6-only, IP-allowlisted, behind a VPN — still verifies. Re-run it until every
|
|
356
356
|
item is green.
|
|
357
357
|
|
|
358
|
-
Your **app** holds only the API key
|
|
358
|
+
Your **app** holds only the API key, never a connection string:
|
|
359
359
|
|
|
360
360
|
```bash
|
|
361
|
-
# .env
|
|
361
|
+
# .env, server runtime only, never the browser
|
|
362
362
|
ABLO_API_KEY=sk_...
|
|
363
|
-
ABLO_PROJECT_ID=proj_...
|
|
364
|
-
ABLO_BRANCH_ID=br_...
|
|
365
363
|
```
|
|
366
364
|
|
|
367
365
|
```ts
|
|
@@ -371,17 +369,17 @@ import { schema } from './ablo/schema';
|
|
|
371
369
|
export const ablo = Ablo({
|
|
372
370
|
schema,
|
|
373
371
|
apiKey: process.env.ABLO_API_KEY,
|
|
374
|
-
projectId: process.env.ABLO_PROJECT_ID,
|
|
375
|
-
branchId: process.env.ABLO_BRANCH_ID,
|
|
376
372
|
});
|
|
377
373
|
```
|
|
378
374
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
375
|
+
The key names its own project and branch, so there is nothing else to configure.
|
|
376
|
+
|
|
377
|
+
If you want a process to refuse a key you did not expect, pin `projectId` or
|
|
378
|
+
`branchId` (they default to `ABLO_PROJECT_ID` and `ABLO_BRANCH_ID`). Both are
|
|
379
|
+
assertions, never routing inputs: during `ready()` Ablo asks the server what the
|
|
380
|
+
key actually targets and refuses to start when a coordinate differs. That is
|
|
381
|
+
worth setting where one deployment can be handed keys for more than one
|
|
382
|
+
environment, and worth leaving out everywhere else.
|
|
385
383
|
|
|
386
384
|
The Ablo schema describes **only your synced, collaborative models** — the rows
|
|
387
385
|
Ablo coordinates and fans out in realtime. It is _not_ your whole-database schema
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.0",
|
|
4
4
|
"description": "The public Ablo SDK for coordinated reads, commits, claims, observation, and reactive applications.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -120,6 +120,8 @@
|
|
|
120
120
|
"lint:pricing": "tsx scripts/check-pricing-docs.mts",
|
|
121
121
|
"generate:openapi": "tsx --conditions=@ablo/source scripts/generate-openapi.mts",
|
|
122
122
|
"lint:openapi": "tsx --conditions=@ablo/source scripts/generate-openapi.mts --check",
|
|
123
|
+
"generate:discovery": "tsx --conditions=@ablo/source scripts/generate-discovery-docs.mts",
|
|
124
|
+
"lint:discovery": "tsx --conditions=@ablo/source scripts/generate-discovery-docs.mts --check",
|
|
123
125
|
"validate:openapi": "redocly lint ../../docs/ablo/public/openapi.json --extends=recommended --skip-rule=no-server-example.com",
|
|
124
126
|
"build:docs": "node scripts/build-blume-docs.mjs",
|
|
125
127
|
"lint:docs-site": "node scripts/build-blume-docs.mjs --check",
|
|
@@ -137,8 +139,8 @@
|
|
|
137
139
|
"directory": "packages/ablo"
|
|
138
140
|
},
|
|
139
141
|
"dependencies": {
|
|
140
|
-
"@abloatai/humans": "^0.
|
|
141
|
-
"@abloatai/transaction": "^0.
|
|
142
|
+
"@abloatai/humans": "^0.56.0",
|
|
143
|
+
"@abloatai/transaction": "^0.56.0",
|
|
142
144
|
"zod": "^4.4.3"
|
|
143
145
|
},
|
|
144
146
|
"peerDependencies": {
|