@abloatai/ablo 0.9.14 → 0.10.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 +20 -0
- package/dist/BaseSyncedStore.d.ts +3 -2
- package/dist/Model.js +9 -1
- package/dist/cli.cjs +3 -4
- package/dist/client/Ablo.d.ts +25 -0
- package/dist/client/Ablo.js +8 -0
- package/dist/environment.d.ts +12 -0
- package/dist/environment.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/keys/index.d.ts +6 -3
- package/dist/keys/index.js +14 -5
- 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/dist/transactions/TransactionQueue.js +6 -0
- package/package.json +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Rename environment enum values to `production` and `sandbox` while preserving the existing `*_live_`/`*_test_` key prefix format.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Stateless HTTP transport for server-side actors, and a canonical environment vocabulary.
|
|
12
|
+
- **`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.
|
|
13
|
+
- **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).
|
|
14
|
+
- **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.
|
|
15
|
+
- **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`.
|
|
16
|
+
|
|
17
|
+
## 0.9.15
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Package metadata: set the npm description to "The Collaboration Layer For AI Agents" (matching the GitHub repo About) so it stops reverting to the old "State control API…" text on publish.
|
|
22
|
+
|
|
3
23
|
## 0.9.14
|
|
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/Model.js
CHANGED
|
@@ -78,11 +78,19 @@ export class Model {
|
|
|
78
78
|
? data.createdAt
|
|
79
79
|
: new Date(data.createdAt)
|
|
80
80
|
: new Date();
|
|
81
|
+
// A record that arrives WITH `createdAt` but WITHOUT `updatedAt` is
|
|
82
|
+
// server/IDB data whose update timestamp didn't survive the wire —
|
|
83
|
+
// falling back to "now" here fabricated an edit time for every such
|
|
84
|
+
// record on every bootstrap (the decks gallery sorted everything to
|
|
85
|
+
// "edited just now"). Fall back to createdAt instead; only a genuinely
|
|
86
|
+
// new local model (no dates at all) stamps the current time.
|
|
81
87
|
this.updatedAt = data.updatedAt
|
|
82
88
|
? data.updatedAt instanceof Date
|
|
83
89
|
? data.updatedAt
|
|
84
90
|
: new Date(data.updatedAt)
|
|
85
|
-
:
|
|
91
|
+
: data.createdAt
|
|
92
|
+
? new Date(this.createdAt)
|
|
93
|
+
: new Date();
|
|
86
94
|
this.syncStatus = data.syncStatus || 'pending';
|
|
87
95
|
}
|
|
88
96
|
/**
|
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 };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const ENVIRONMENTS: readonly ["production", "sandbox"];
|
|
3
|
+
export type KeyPrefixEnvironment = 'live' | 'test';
|
|
4
|
+
export declare const environmentSchema: z.ZodEnum<{
|
|
5
|
+
production: "production";
|
|
6
|
+
sandbox: "sandbox";
|
|
7
|
+
}>;
|
|
8
|
+
export type Environment = z.infer<typeof environmentSchema>;
|
|
9
|
+
export declare function normalizeEnvironment(value: unknown, fallback?: Environment): Environment;
|
|
10
|
+
export declare function environmentFromKeyPrefix(value: KeyPrefixEnvironment): Environment;
|
|
11
|
+
export declare function environmentToKeyPrefix(value: Environment): KeyPrefixEnvironment;
|
|
12
|
+
export declare function isSandboxEnvironment(value: Environment): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const ENVIRONMENTS = ['production', 'sandbox'];
|
|
3
|
+
export const environmentSchema = z.enum(ENVIRONMENTS);
|
|
4
|
+
export function normalizeEnvironment(value, fallback = 'production') {
|
|
5
|
+
const parsed = environmentSchema.safeParse(value);
|
|
6
|
+
return parsed.success ? parsed.data : fallback;
|
|
7
|
+
}
|
|
8
|
+
export function environmentFromKeyPrefix(value) {
|
|
9
|
+
return value === 'test' ? 'sandbox' : 'production';
|
|
10
|
+
}
|
|
11
|
+
export function environmentToKeyPrefix(value) {
|
|
12
|
+
return value === 'sandbox' ? 'test' : 'live';
|
|
13
|
+
}
|
|
14
|
+
export function isSandboxEnvironment(value) {
|
|
15
|
+
return value === 'sandbox';
|
|
16
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,8 @@ export { SyncSessionError, AbloError, AbloAuthenticationError, AbloPermissionErr
|
|
|
64
64
|
export type { CommitReceipt, RequiredCapability } from './errors.js';
|
|
65
65
|
export type { ErrorCode, WireErrorCode, ErrorCategory, ErrorCodeSpec, RecoveryClass } from './errors.js';
|
|
66
66
|
export { WS_BEARER_SUBPROTOCOL_PREFIX, WS_SYNC_SUBPROTOCOL } from './auth/credentialSource.js';
|
|
67
|
+
export { ENVIRONMENTS, environmentSchema, normalizeEnvironment, environmentFromKeyPrefix, environmentToKeyPrefix, isSandboxEnvironment, } from './environment.js';
|
|
68
|
+
export type { Environment, KeyPrefixEnvironment } from './environment.js';
|
|
67
69
|
export { writeOptionsSchema, onStaleModeSchema, assertWriteOptions, } from './client/writeOptionsSchema.js';
|
|
68
70
|
export type { WriteOptionsInput } from './client/writeOptionsSchema.js';
|
|
69
71
|
export type { WriteOptions, MutationOptions } from './interfaces/index.js';
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,7 @@ export { defaultPolicy, capabilityPreemptPolicy } from './policy/index.js';
|
|
|
89
89
|
// `e.type === 'AbloX'`) plus the HTTP-response translator.
|
|
90
90
|
export { SyncSessionError, AbloError, AbloAuthenticationError, AbloPermissionError, AbloRateLimitError, AbloIdempotencyError, AbloConnectionError, AbloValidationError, AbloServerError, AbloStaleContextError, AbloClaimedError, CapabilityError, translateHttpError, hasWireCode, errorFromWire, toAbloError, ERROR_CODES, ERROR_CONTRACT_VERSION, errorCodeSpec, isRetryableCode, classifyRecovery, recoveryClassSchema, RECOVERY_CLASSES, } from './errors.js';
|
|
91
91
|
export { WS_BEARER_SUBPROTOCOL_PREFIX, WS_SYNC_SUBPROTOCOL } from './auth/credentialSource.js';
|
|
92
|
+
export { ENVIRONMENTS, environmentSchema, normalizeEnvironment, environmentFromKeyPrefix, environmentToKeyPrefix, isSandboxEnvironment, } from './environment.js';
|
|
92
93
|
// THE write-options contract — the one Zod schema for the option bag every
|
|
93
94
|
// write door accepts (`ablo.<model>.create/update/delete`, `commits.create`,
|
|
94
95
|
// the HTTP model routes). The SDK validates against it at each boundary;
|
package/dist/keys/index.d.ts
CHANGED
|
@@ -10,16 +10,19 @@
|
|
|
10
10
|
* client bundle never pulls in `node:crypto`.
|
|
11
11
|
*
|
|
12
12
|
* Format (GitHub-style): `<sk|rk|ek>_<live|test>_<30 base62 body><6-char
|
|
13
|
-
* base62 CRC32 checksum>`. The
|
|
13
|
+
* base62 CRC32 checksum>`. The environment segment is the stable key-prefix
|
|
14
|
+
* contract; parsed values are immediately mapped to `production` / `sandbox`.
|
|
15
|
+
* The identifiable prefix + CRC32 checksum let
|
|
14
16
|
* secret scanners detect leaks and let us reject typo'd/forged keys OFFLINE
|
|
15
17
|
* (no DB round-trip). Legacy keys (a ~43-char base64url body, no checksum)
|
|
16
18
|
* still validate by hash — they parse here as `checksummed: false`.
|
|
17
19
|
*/
|
|
18
20
|
import { z } from 'zod';
|
|
21
|
+
import { type Environment } from '../environment.js';
|
|
19
22
|
export declare const API_KEY_KINDS: readonly ["secret", "restricted", "ephemeral", "publishable"];
|
|
20
23
|
export type ApiKeyKind = (typeof API_KEY_KINDS)[number];
|
|
21
|
-
export declare const API_KEY_ENVS: readonly ["
|
|
22
|
-
export type ApiKeyEnv =
|
|
24
|
+
export declare const API_KEY_ENVS: readonly ["production", "sandbox"];
|
|
25
|
+
export type ApiKeyEnv = Environment;
|
|
23
26
|
/** A structurally-valid Ablo API key, parsed into its parts. */
|
|
24
27
|
export interface ParsedApiKey {
|
|
25
28
|
/** The original plaintext. */
|
package/dist/keys/index.js
CHANGED
|
@@ -10,13 +10,16 @@
|
|
|
10
10
|
* client bundle never pulls in `node:crypto`.
|
|
11
11
|
*
|
|
12
12
|
* Format (GitHub-style): `<sk|rk|ek>_<live|test>_<30 base62 body><6-char
|
|
13
|
-
* base62 CRC32 checksum>`. The
|
|
13
|
+
* base62 CRC32 checksum>`. The environment segment is the stable key-prefix
|
|
14
|
+
* contract; parsed values are immediately mapped to `production` / `sandbox`.
|
|
15
|
+
* The identifiable prefix + CRC32 checksum let
|
|
14
16
|
* secret scanners detect leaks and let us reject typo'd/forged keys OFFLINE
|
|
15
17
|
* (no DB round-trip). Legacy keys (a ~43-char base64url body, no checksum)
|
|
16
18
|
* still validate by hash — they parse here as `checksummed: false`.
|
|
17
19
|
*/
|
|
18
20
|
import { createHash, randomBytes } from 'node:crypto';
|
|
19
21
|
import { z } from 'zod';
|
|
22
|
+
import { ENVIRONMENTS, environmentFromKeyPrefix, environmentToKeyPrefix, } from '../environment.js';
|
|
20
23
|
// ── Vocabulary ──────────────────────────────────────────────────────────
|
|
21
24
|
// The Stripe-style key model:
|
|
22
25
|
// secret (sk_) — backend / server-to-server / agents. Full authority. Never in a browser.
|
|
@@ -29,7 +32,7 @@ import { z } from 'zod';
|
|
|
29
32
|
// it; it grants read access to the org's data plane and cannot write
|
|
30
33
|
// or reach any control-plane operation.
|
|
31
34
|
export const API_KEY_KINDS = ['secret', 'restricted', 'ephemeral', 'publishable'];
|
|
32
|
-
export const API_KEY_ENVS =
|
|
35
|
+
export const API_KEY_ENVS = ENVIRONMENTS;
|
|
33
36
|
const PREFIX_BY_KIND = {
|
|
34
37
|
secret: 'sk',
|
|
35
38
|
restricted: 'rk',
|
|
@@ -115,7 +118,13 @@ export const apiKeySchema = z.string().transform((raw, ctx) => {
|
|
|
115
118
|
ctx.addIssue({ code: 'custom', message: 'API key checksum mismatch' });
|
|
116
119
|
return z.NEVER;
|
|
117
120
|
}
|
|
118
|
-
return {
|
|
121
|
+
return {
|
|
122
|
+
raw,
|
|
123
|
+
kind: KIND_BY_PREFIX[prefix],
|
|
124
|
+
env: environmentFromKeyPrefix(env),
|
|
125
|
+
body,
|
|
126
|
+
checksummed,
|
|
127
|
+
};
|
|
119
128
|
});
|
|
120
129
|
// ── Derived validators (thin wrappers over the same spec) ───────────────
|
|
121
130
|
/** Parse + fully validate (incl. checksum). Returns null when invalid. */
|
|
@@ -140,9 +149,9 @@ export function keyChecksumMatches(raw) {
|
|
|
140
149
|
* Mint a key: `<prefix>_<env>_<body><checksum>`. Returns the plaintext (shown
|
|
141
150
|
* once), its SHA-256 hash (persisted), and the 12-char display prefix.
|
|
142
151
|
*/
|
|
143
|
-
export function generateApiKey(env = '
|
|
152
|
+
export function generateApiKey(env = 'production', kind = 'secret') {
|
|
144
153
|
const body = randomBase62(KEY_BODY_LEN);
|
|
145
|
-
const payload = `${PREFIX_BY_KIND[kind]}_${env}_${body}`;
|
|
154
|
+
const payload = `${PREFIX_BY_KIND[kind]}_${environmentToKeyPrefix(env)}_${body}`;
|
|
146
155
|
const plaintext = `${payload}${checksum6(payload)}`;
|
|
147
156
|
return { plaintext, hash: hashApiKey(plaintext), prefix: plaintext.slice(0, 12) };
|
|
148
157
|
}
|
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
|
/**
|
|
@@ -949,6 +949,12 @@ export class TransactionQueue extends EventEmitter {
|
|
|
949
949
|
// Ensure derived fields exist (covers restored/persisted transactions)
|
|
950
950
|
this.ensureDerivedFields(a);
|
|
951
951
|
this.ensureDerivedFields(b);
|
|
952
|
+
if (a.modelName === b.modelName && a.modelId === b.modelId && a.type !== b.type) {
|
|
953
|
+
if (a.type === 'create')
|
|
954
|
+
return -1;
|
|
955
|
+
if (b.type === 'create')
|
|
956
|
+
return 1;
|
|
957
|
+
}
|
|
952
958
|
return a.priorityScore - b.priorityScore;
|
|
953
959
|
});
|
|
954
960
|
// Get batch (now guaranteed to have parent entities before children)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/ablo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "The Collaboration Layer For AI Agents",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -83,6 +83,11 @@
|
|
|
83
83
|
"import": "./dist/keys/index.js",
|
|
84
84
|
"default": "./dist/keys/index.js"
|
|
85
85
|
},
|
|
86
|
+
"./environment": {
|
|
87
|
+
"types": "./dist/environment.d.ts",
|
|
88
|
+
"import": "./dist/environment.js",
|
|
89
|
+
"default": "./dist/environment.js"
|
|
90
|
+
},
|
|
86
91
|
"./wire": {
|
|
87
92
|
"types": "./dist/wire/index.d.ts",
|
|
88
93
|
"import": "./dist/wire/index.js",
|