@abloatai/ablo 0.9.15 → 0.10.1
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 +20 -0
- package/dist/BaseSyncedStore.d.ts +3 -2
- package/dist/cli.cjs +3 -4
- package/dist/client/Ablo.d.ts +25 -0
- package/dist/client/Ablo.js +8 -0
- package/dist/schema/diff.d.ts +1 -1
- package/dist/server/commit.d.ts +14 -0
- package/dist/source/index.d.ts +6 -5
- package/dist/sync/SyncWebSocket.d.ts +4 -5
- package/docs/migration.md +52 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Docs: add the 0.10.0 entry to the Version History & Migration Guide — the `test`/`live` → `sandbox`/`production` environment enum rename (key prefixes unchanged) and the new `transport: 'http'` stateless client.
|
|
8
|
+
|
|
9
|
+
## 0.10.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- Rename environment enum values to `production` and `sandbox` while preserving the existing `*_live_`/`*_test_` key prefix format.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Stateless HTTP transport for server-side actors, and a canonical environment vocabulary.
|
|
18
|
+
- **`Ablo({ transport: 'http' })`** returns a stateless `AbloHttpClient` for agents, workers, and serverless — the same `ablo.<model>` surface and coordination plane with no websocket: each call is one HTTP round-trip and identity rides the Bearer credential. The return type narrows so stateful-only APIs (`get`/`getAll`/`onChange`) are compile errors instead of latent runtime gaps.
|
|
19
|
+
- **Canonical `production` / `sandbox` environments** (new `environment.ts`, exported from the root): `sk_test_` / `sk_live_` remain the wire-level key prefixes but now map to `production` / `sandbox` everywhere — key parsing, source `mode`, and the CLI (which drops the legacy test/live config migration).
|
|
20
|
+
- **Source-mode commit scoping**: `commit` now forwards `projectId`, `accountScope`, and `environment` to customer storage resolvers, so per-project and sandbox/production traffic can be routed to distinct stores.
|
|
21
|
+
- **Fixes**: the WebSocket bearer credential is sent in the `ablo.bearer.<token>` subprotocol (never in the URL or proxy logs); `Model` no longer fabricates an `updatedAt` of "now" for records that arrive with only `createdAt`.
|
|
22
|
+
|
|
3
23
|
## 0.9.15
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -125,8 +125,9 @@ export interface UserContext {
|
|
|
125
125
|
* `kind=agent` and the server applies capability-token auth. */
|
|
126
126
|
kind?: 'user' | 'agent' | 'system';
|
|
127
127
|
/** Restricted (`rk_`) API key for `kind: 'agent'` — the agent's
|
|
128
|
-
* bearer credential. Sent
|
|
129
|
-
*
|
|
128
|
+
* bearer credential. Sent in the `ablo.bearer.<token>` WebSocket
|
|
129
|
+
* subprotocol, never in the URL. (Field name predates the
|
|
130
|
+
* Biscuit→opaque-key migration.) */
|
|
130
131
|
capabilityToken?: string;
|
|
131
132
|
/** Server-authoritative sync groups, supplied by auth/capability
|
|
132
133
|
* exchange. The SDK does not invent org/user/default groups; app
|
package/dist/cli.cjs
CHANGED
|
@@ -279453,13 +279453,12 @@ function asActiveProject(value) {
|
|
|
279453
279453
|
return void 0;
|
|
279454
279454
|
}
|
|
279455
279455
|
function normalizeStoredMode(value) {
|
|
279456
|
-
if (value === "sandbox" || value === "
|
|
279457
|
-
if (value === "production" || value === "live") return "production";
|
|
279456
|
+
if (value === "sandbox" || value === "production") return value;
|
|
279458
279457
|
return void 0;
|
|
279459
279458
|
}
|
|
279460
279459
|
function extractEntries(obj) {
|
|
279461
|
-
const sandbox = asKeyEntry(obj.sandbox)
|
|
279462
|
-
const production = asKeyEntry(obj.production)
|
|
279460
|
+
const sandbox = asKeyEntry(obj.sandbox);
|
|
279461
|
+
const production = asKeyEntry(obj.production);
|
|
279463
279462
|
if (sandbox || production) {
|
|
279464
279463
|
return { ...sandbox ? { sandbox } : {}, ...production ? { production } : {} };
|
|
279465
279464
|
}
|
package/dist/client/Ablo.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type { IntentStream, IntentWaitOptions, PresenceStream, Snapshot } from '
|
|
|
29
29
|
import type { ParticipantManager } from '../sync/participants.js';
|
|
30
30
|
import type { ActiveIntent, Duration, Intent, TargetRange } from '../types/streams.js';
|
|
31
31
|
import { type AbloApi, type AbloApiClientOptions, type AbloApiIntents } from './ApiClient.js';
|
|
32
|
+
import { type AbloHttpClient, type AbloHttpClientOptions } from './httpClient.js';
|
|
32
33
|
/**
|
|
33
34
|
* Async function that resolves an apiKey at request time. Use for
|
|
34
35
|
* credential rotation — rotate from a vault, refresh from session
|
|
@@ -125,6 +126,19 @@ export interface AbloOptions<S extends SchemaRecord = SchemaRecord> {
|
|
|
125
126
|
* @default 'memory'
|
|
126
127
|
*/
|
|
127
128
|
persistence?: AbloPersistence;
|
|
129
|
+
/**
|
|
130
|
+
* Transport selector. `'websocket'` (default) is the live client —
|
|
131
|
+
* persistent socket, local synced pool, `onChange` subscriptions. `'http'`
|
|
132
|
+
* returns the STATELESS client for server-side actors (agents, workers,
|
|
133
|
+
* serverless): same `ablo.<model>` surface and coordination plane, but each
|
|
134
|
+
* call is one HTTP round-trip, identity rides the Bearer credential, and no
|
|
135
|
+
* socket is ever opened. With `'http'` the return type narrows to
|
|
136
|
+
* `AbloHttpClient<S>`, so stateful-only capabilities (`get`/`getAll`,
|
|
137
|
+
* `onChange`) are compile errors rather than latent runtime gaps.
|
|
138
|
+
*
|
|
139
|
+
* @default 'websocket'
|
|
140
|
+
*/
|
|
141
|
+
transport?: 'websocket' | 'http' | undefined;
|
|
128
142
|
/**
|
|
129
143
|
* Bearer auth token. Hosted-cloud consumers pass `apiKey`; self-hosted
|
|
130
144
|
* deployments may pass a bearer token minted by their own auth layer.
|
|
@@ -944,7 +958,18 @@ export declare function computeFKDepthPriority(schema: Schema): ReadonlyMap<stri
|
|
|
944
958
|
* const reports = sync.weatherReports.list({ where: { status: 'pending' } });
|
|
945
959
|
* await sync.weatherReports.create({ location: 'Stockholm', status: 'pending' });
|
|
946
960
|
* ```
|
|
961
|
+
*
|
|
962
|
+
* Pass `transport: 'http'` for the stateless server-side client (agents,
|
|
963
|
+
* workers, serverless) — same `ablo.<model>` surface, no socket:
|
|
964
|
+
*
|
|
965
|
+
* ```ts
|
|
966
|
+
* const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, transport: 'http' });
|
|
967
|
+
* await ablo.tasks.update({ id, data: { status: 'done' } });
|
|
968
|
+
* ```
|
|
947
969
|
*/
|
|
970
|
+
export declare function Ablo<const S extends SchemaRecord>(options: AbloHttpClientOptions<S> & {
|
|
971
|
+
transport: 'http';
|
|
972
|
+
}): AbloHttpClient<S>;
|
|
948
973
|
export declare function Ablo<const S extends SchemaRecord>(options: AbloOptions<S>): Ablo<S>;
|
|
949
974
|
export declare function Ablo(options: AbloApiClientOptions): AbloApi;
|
|
950
975
|
import type * as _Streams from '../types/streams.js';
|
package/dist/client/Ablo.js
CHANGED
|
@@ -37,6 +37,9 @@ import { awaitIntentGrant } from '../sync/awaitIntentGrant.js';
|
|
|
37
37
|
import { createSnapshot } from '../sync/createSnapshot.js';
|
|
38
38
|
import { createParticipantManager } from '../sync/participants.js';
|
|
39
39
|
import { createProtocolClient, } from './ApiClient.js';
|
|
40
|
+
// Value import is cycle-safe: httpClient.js only value-imports ApiClient.js,
|
|
41
|
+
// which imports this module type-only.
|
|
42
|
+
import { createAbloHttpClient, } from './httpClient.js';
|
|
40
43
|
import { assertBrowserSafety, readProcessEnv, resolveApiKey, resolveApiKeyValue, resolveAuthToken, resolveBaseURL, resolveBootstrapBaseUrl, resolveDatabaseUrl, } from './auth.js';
|
|
41
44
|
import { registerDataSource } from './registerDataSource.js';
|
|
42
45
|
import { shouldUseInMemoryPersistence, } from './persistence.js';
|
|
@@ -684,8 +687,13 @@ function resolveCredentialResolver(options) {
|
|
|
684
687
|
}
|
|
685
688
|
export function Ablo(options) {
|
|
686
689
|
if (options.schema == null) {
|
|
690
|
+
// The protocol client IS the stateless HTTP plane (string-keyed models),
|
|
691
|
+
// so `transport: 'http'` needs no special-casing here.
|
|
687
692
|
return createProtocolClient(options);
|
|
688
693
|
}
|
|
694
|
+
if (options.transport === 'http') {
|
|
695
|
+
return createAbloHttpClient(options);
|
|
696
|
+
}
|
|
689
697
|
const internalOptions = options;
|
|
690
698
|
const env = readProcessEnv();
|
|
691
699
|
const authInput = { options, env };
|
package/dist/schema/diff.d.ts
CHANGED
|
@@ -124,7 +124,7 @@ export type WarningCode = 'drop_model' | 'drop_field' | 'risky_cast' | 'lossy_re
|
|
|
124
124
|
/** A model disappears from what this plane's READERS resolve, without any
|
|
125
125
|
* table being dropped. Emitted by the server's push gate (not
|
|
126
126
|
* `classifyMigration`) when a first sandbox push shadows the production
|
|
127
|
-
* artifact that sandbox readers were served via the registry's
|
|
127
|
+
* artifact that sandbox readers were served via the registry's sandbox→production
|
|
128
128
|
* fallback. The data plane is untouched — the loss is visibility. */
|
|
129
129
|
| 'remove_model';
|
|
130
130
|
export type BlockerCode = 'required_field_added' | 'made_required';
|
package/dist/server/commit.d.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import type { ParticipantKind, ConfirmationState } from '../schema/sync-delta-row.js';
|
|
17
17
|
import type { ParticipantRef } from '../schema/sync-delta-wire.js';
|
|
18
|
+
import type { Environment } from '../environment.js';
|
|
18
19
|
export interface CommitContext {
|
|
19
20
|
participantId: string;
|
|
20
21
|
/**
|
|
@@ -24,6 +25,19 @@ export interface CommitContext {
|
|
|
24
25
|
*/
|
|
25
26
|
participantKind: ParticipantKind;
|
|
26
27
|
organizationId: string;
|
|
28
|
+
/**
|
|
29
|
+
* Product/project scope for routing source-mode storage. Omitted means the
|
|
30
|
+
* org-default project (the legacy behavior).
|
|
31
|
+
*/
|
|
32
|
+
projectId?: string;
|
|
33
|
+
/** Optional external account scope forwarded to storage resolvers. */
|
|
34
|
+
accountScope?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Canonical environment for this commit. Source-mode adapters forward this to
|
|
37
|
+
* customer handlers so sandbox and production traffic can hit distinct
|
|
38
|
+
* customer-owned stores.
|
|
39
|
+
*/
|
|
40
|
+
environment?: Environment;
|
|
27
41
|
/**
|
|
28
42
|
* The participant's own subscribed sync groups (from the WS upgrade or
|
|
29
43
|
* capability token). Appended to every delta's `sync_groups` so writes fan
|
package/dist/source/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Schema, SchemaRecord, InferCreate } from '../schema/schema.js';
|
|
2
|
+
import type { Environment } from '../environment.js';
|
|
2
3
|
import type { DataSourceAdapter } from './adapter.js';
|
|
3
4
|
export type SourcePrimitive = string | number | boolean | null;
|
|
4
5
|
export type SourceWhere = readonly [field: string, value: SourcePrimitive] | readonly [
|
|
@@ -47,21 +48,21 @@ export interface SourceRequestContext {
|
|
|
47
48
|
readonly organizationId?: string;
|
|
48
49
|
readonly requiredSyncGroups?: readonly string[];
|
|
49
50
|
/**
|
|
50
|
-
*
|
|
51
|
-
* handlers on this (`if (mode === '
|
|
51
|
+
* Production/sandbox mode for this request. Customers branch their source
|
|
52
|
+
* handlers on this (`if (mode === 'sandbox') db = sandboxDb`) so sandbox
|
|
52
53
|
* traffic exercises the same code path against an isolated store.
|
|
53
54
|
*
|
|
54
|
-
* Mirrors Stripe's `sk_test_` / `sk_live_`
|
|
55
|
+
* Mirrors Stripe's `sk_test_` / `sk_live_` prefixes: same wire
|
|
55
56
|
* shape, same handler code, different namespace. Ablo's server-side
|
|
56
57
|
* fan-out does not yet partition deltas by mode — that lands when
|
|
57
58
|
* `sync_deltas.mode` ships. Until then, isolation is enforced
|
|
58
59
|
* customer-side via this field, which is the right boundary anyway
|
|
59
60
|
* (the customer's database is where the canonical data lives).
|
|
60
61
|
*
|
|
61
|
-
* Defaults to `'
|
|
62
|
+
* Defaults to `'production'` when omitted so callers that don't opt in
|
|
62
63
|
* keep the existing behavior.
|
|
63
64
|
*/
|
|
64
|
-
readonly mode?:
|
|
65
|
+
readonly mode?: Environment;
|
|
65
66
|
}
|
|
66
67
|
export interface SourceOperation {
|
|
67
68
|
readonly type: 'CREATE' | 'UPDATE' | 'DELETE' | 'ARCHIVE' | 'UNARCHIVE';
|
|
@@ -92,11 +92,10 @@ export interface SyncWebSocketOptions {
|
|
|
92
92
|
kind?: 'user' | 'agent' | 'system';
|
|
93
93
|
/**
|
|
94
94
|
* The agent's bearer credential — a restricted (`rk_`) API key. When
|
|
95
|
-
* set, sent
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* the Biscuit→opaque-key migration.)
|
|
95
|
+
* set, sent in the `ablo.bearer.<token>` WebSocket subprotocol so the
|
|
96
|
+
* credential stays out of URLs and proxy logs. Required for `kind: 'agent'`;
|
|
97
|
+
* ignored for `kind: 'user'`. (Field name predates the Biscuit→opaque-key
|
|
98
|
+
* migration.)
|
|
100
99
|
*/
|
|
101
100
|
capabilityToken?: string;
|
|
102
101
|
/**
|
package/docs/migration.md
CHANGED
|
@@ -11,6 +11,7 @@ change when you upgrade.
|
|
|
11
11
|
|
|
12
12
|
| Version | What changed | What to do |
|
|
13
13
|
|---|---|---|
|
|
14
|
+
| **0.10.0** | Environment enum renamed `test`/`live` → `sandbox`/`production` | Update code that branches on the environment (e.g. source `mode`): `'test'`→`'sandbox'`, `'live'`→`'production'`. Key prefixes `sk_test_`/`sk_live_` are unchanged |
|
|
14
15
|
| **0.9.2** | `turn` primitive + agent-work `tasks` resource removed | Coordinate with `claim`; mint a scoped session instead of `agent().run()` |
|
|
15
16
|
| **0.9.2** | `intents` deprecated in favor of `claim` | Use `ablo.<model>.claim`; `ablo.intents` is now `@internal` |
|
|
16
17
|
| **0.9.0** | One options object per verb | `update(id, data, opts)` → `update({ id, data, ...opts })` |
|
|
@@ -23,6 +24,57 @@ change when you upgrade.
|
|
|
23
24
|
|
|
24
25
|
---
|
|
25
26
|
|
|
27
|
+
## 0.10.0 — environment enum `sandbox` / `production`; stateless HTTP transport
|
|
28
|
+
|
|
29
|
+
### Environment enum rename (the only breaking change)
|
|
30
|
+
|
|
31
|
+
The canonical environment values are now **`production`** and **`sandbox`** (was
|
|
32
|
+
`live` and `test`). This is a *vocabulary* change at the type/API layer — the
|
|
33
|
+
on-the-wire key prefixes are **unchanged**: keys are still `sk_test_…` /
|
|
34
|
+
`sk_live_…` and parse exactly as before. What changed is the enum you see in
|
|
35
|
+
code: `Environment`, the source-handler `mode` field, and `ApiKeyEnv` now read
|
|
36
|
+
`production` / `sandbox`.
|
|
37
|
+
|
|
38
|
+
You only need to act if your code branches on the environment value — most
|
|
39
|
+
commonly a Data Source handler keyed on `mode`. The mapping is exactly
|
|
40
|
+
`test → sandbox`, `live → production`:
|
|
41
|
+
|
|
42
|
+
```diff
|
|
43
|
+
const handler = createSourceHandler({
|
|
44
|
+
read: async ({ mode }) => {
|
|
45
|
+
- const db = mode === 'test' ? testDb : liveDb;
|
|
46
|
+
+ const db = mode === 'sandbox' ? sandboxDb : productionDb;
|
|
47
|
+
// …
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`commit` now also forwards `projectId`, `accountScope`, and `environment` to
|
|
53
|
+
source resolvers, so per-project and per-environment traffic can be routed to
|
|
54
|
+
distinct stores.
|
|
55
|
+
|
|
56
|
+
> **CLI note:** the legacy single-file config that stored `test`/`live` key
|
|
57
|
+
> buckets is no longer auto-migrated. If `ablo status` can't find your keys after
|
|
58
|
+
> upgrading, re-run `ablo login` to write the current `sandbox`/`production`
|
|
59
|
+
> layout.
|
|
60
|
+
|
|
61
|
+
### New (non-breaking): `transport: 'http'`
|
|
62
|
+
|
|
63
|
+
`Ablo({ transport: 'http' })` returns a stateless `AbloHttpClient` for
|
|
64
|
+
server-side actors (agents, workers, serverless): the same `ablo.<model>` surface
|
|
65
|
+
and `claim` coordination, but each call is one HTTP round-trip with identity on
|
|
66
|
+
the Bearer credential — no websocket, no local synced pool. The return type
|
|
67
|
+
narrows, so stateful-only APIs (`get` / `getAll` / `onChange`) become compile
|
|
68
|
+
errors instead of latent runtime gaps. Existing code keeps the default
|
|
69
|
+
`'websocket'` transport, unchanged.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const ablo = Ablo({ schema, apiKey: process.env.ABLO_API_KEY, transport: 'http' });
|
|
73
|
+
await ablo.tasks.update({ id, data: { status: 'done' } });
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
26
78
|
## 0.9.2 — `turn` / agent-`tasks` removed; `intents` deprecated
|
|
27
79
|
|
|
28
80
|
The SDK's coordination surface is now exactly two things: `ablo.<model>` writes
|