@abloatai/transaction 0.46.0 → 0.48.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/dist/ai-sdk/modelTools.d.ts.map +1 -1
- package/dist/ai-sdk/modelTools.js +1 -2
- package/dist/ai-sdk/modelTools.js.map +1 -1
- package/dist/ai-sdk/updateTool.d.ts.map +1 -1
- package/dist/ai-sdk/updateTool.js +0 -1
- package/dist/ai-sdk/updateTool.js.map +1 -1
- package/dist/auth/capability.d.ts +8 -0
- package/dist/auth/capability.d.ts.map +1 -1
- package/dist/auth/capability.js +2 -0
- package/dist/auth/capability.js.map +1 -1
- package/dist/auth/capabilityLifecycle.d.ts +4 -0
- package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
- package/dist/auth/deliveryPartition.d.ts +14 -0
- package/dist/auth/deliveryPartition.d.ts.map +1 -0
- package/dist/auth/deliveryPartition.js +16 -0
- package/dist/auth/deliveryPartition.js.map +1 -0
- package/dist/auth/identity.d.ts +2 -0
- package/dist/auth/identity.d.ts.map +1 -1
- package/dist/auth/identity.js +3 -0
- package/dist/auth/identity.js.map +1 -1
- package/dist/auth/schemas.d.ts +8 -0
- package/dist/auth/schemas.d.ts.map +1 -1
- package/dist/auth/schemas.js +3 -0
- package/dist/auth/schemas.js.map +1 -1
- package/dist/auth/sessionMint.d.ts.map +1 -1
- package/dist/auth/sessionMint.js +1 -0
- package/dist/auth/sessionMint.js.map +1 -1
- package/dist/branches.d.ts +61 -12
- package/dist/branches.d.ts.map +1 -1
- package/dist/branches.js +25 -3
- package/dist/branches.js.map +1 -1
- package/dist/errorCodes.d.ts +18 -2
- package/dist/errorCodes.d.ts.map +1 -1
- package/dist/errorCodes.js +28 -12
- package/dist/errorCodes.js.map +1 -1
- package/dist/footprint.d.ts +10 -22
- package/dist/footprint.d.ts.map +1 -1
- package/dist/footprint.js +14 -24
- package/dist/footprint.js.map +1 -1
- package/dist/pricing.d.ts +21 -2
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +32 -8
- package/dist/pricing.js.map +1 -1
- package/dist/resources/httpResources.d.ts +0 -1
- package/dist/resources/httpResources.d.ts.map +1 -1
- package/dist/resources/modelOperations.d.ts +13 -3
- package/dist/resources/modelOperations.d.ts.map +1 -1
- package/dist/schema/openapi.js +4 -4
- package/dist/schema/openapi.js.map +1 -1
- package/dist/source/index.d.ts +1 -1
- package/dist/source/index.d.ts.map +1 -1
- package/dist/source/migrations.d.ts +1 -1
- package/dist/source/migrations.d.ts.map +1 -1
- package/dist/source/migrations.js +18 -7
- package/dist/source/migrations.js.map +1 -1
- package/dist/transport/httpTransport.d.ts.map +1 -1
- package/dist/transport/httpTransport.js +5 -10
- package/dist/transport/httpTransport.js.map +1 -1
- package/dist/transport/wsTransport.d.ts +8 -0
- package/dist/transport/wsTransport.d.ts.map +1 -1
- package/dist/transport/wsTransport.js +8 -0
- package/dist/transport/wsTransport.js.map +1 -1
- package/dist/wire/dataSourceResponses.d.ts +17 -1
- package/dist/wire/dataSourceResponses.d.ts.map +1 -1
- package/dist/wire/dataSourceResponses.js +18 -0
- package/dist/wire/dataSourceResponses.js.map +1 -1
- package/dist/wire/index.d.ts +2 -2
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +1 -1
- package/dist/wire/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ai-sdk/modelTools.ts +1 -2
- package/src/ai-sdk/updateTool.ts +0 -1
- package/src/auth/capability.ts +2 -0
- package/src/auth/deliveryPartition.ts +18 -0
- package/src/auth/identity.ts +5 -0
- package/src/auth/schemas.ts +3 -0
- package/src/auth/sessionMint.ts +1 -0
- package/src/branches.ts +27 -3
- package/src/errorCodes.ts +113 -17
- package/src/footprint.ts +18 -27
- package/src/pricing.ts +36 -8
- package/src/resources/httpResources.ts +0 -1
- package/src/resources/modelOperations.ts +14 -3
- package/src/schema/openapi.ts +4 -4
- package/src/source/index.ts +1 -1
- package/src/source/migrations.ts +21 -7
- package/src/transport/httpTransport.ts +5 -10
- package/src/transport/wsTransport.ts +18 -0
- package/src/wire/dataSourceResponses.ts +20 -0
- package/src/wire/index.ts +2 -0
package/dist/wire/index.js
CHANGED
|
@@ -78,7 +78,7 @@ export { projectResponseSchema, projectListResponseSchema, provisionedKeySchema,
|
|
|
78
78
|
// The datasource routes' responses — the `ablo connect` surface: register,
|
|
79
79
|
// validate, locate, list, deregister. What the server, the CLI, and the
|
|
80
80
|
// dashboard agree on.
|
|
81
|
-
export { READINESS_ITEMS, READINESS_ADVISORY_ITEMS, isReadinessItem, readinessFailureSchema, readinessAdvisorySchema, datasourceSummarySchema, datasourceListResponseSchema, datasourceValidationResponseSchema, datasourceLocationResponseSchema, datasourceDisconnectedResponseSchema, } from './dataSourceResponses.js';
|
|
81
|
+
export { READINESS_ITEMS, READINESS_ADVISORY_ITEMS, isReadinessItem, readinessFailureSchema, readinessAdvisorySchema, datasourceSummarySchema, datasourceListResponseSchema, datasourceValidationResponseSchema, datasourceLocationResponseSchema, datasourceResnapshotResponseSchema, datasourceDisconnectedResponseSchema, } from './dataSourceResponses.js';
|
|
82
82
|
// The inbound socket surface: every frame the server can send, and how each
|
|
83
83
|
// one's payload is validated.
|
|
84
84
|
export { WS_INBOUND_FRAMES, wsInboundEnvelopeSchema, isKnownInboundFrame, isSchemaValidatedFrame, } from './inboundFrames.js';
|
package/dist/wire/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/transaction",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.48.0",
|
|
4
4
|
"description": "The headless Ablo transaction client and canonical contracts for reads, commits, settlement, claims, and durable observation.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
package/src/ai-sdk/modelTools.ts
CHANGED
|
@@ -100,7 +100,6 @@ export function createTool<TInput, T, CreateInput>(
|
|
|
100
100
|
const row = await model.create({
|
|
101
101
|
data: options.data(input),
|
|
102
102
|
...(id !== undefined ? { id } : {}),
|
|
103
|
-
wait: 'confirmed',
|
|
104
103
|
});
|
|
105
104
|
return { status: 'created', row };
|
|
106
105
|
},
|
|
@@ -156,7 +155,7 @@ export function deleteTool<TInput, T, Fields = T>(
|
|
|
156
155
|
};
|
|
157
156
|
}
|
|
158
157
|
try {
|
|
159
|
-
await model.delete({ id, claim
|
|
158
|
+
await model.delete({ id, claim });
|
|
160
159
|
return { status: 'deleted', id };
|
|
161
160
|
} finally {
|
|
162
161
|
await claim.release();
|
package/src/ai-sdk/updateTool.ts
CHANGED
package/src/auth/capability.ts
CHANGED
|
@@ -27,6 +27,7 @@ import { z } from 'zod';
|
|
|
27
27
|
import { participantKindSchema } from '../coordination/schema.js';
|
|
28
28
|
import { syncGroupInputSchema } from '../schema/roles.js';
|
|
29
29
|
import { authTokenSchema } from './token.js';
|
|
30
|
+
import { deliveryPartitionRouteSchema } from './deliveryPartition.js';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* The verbs a grant can name — the whole vocabulary, in one place. Every other
|
|
@@ -253,6 +254,7 @@ export const capabilityScopeSchema = z.object({
|
|
|
253
254
|
operations: z.array(grantedOperationSchema),
|
|
254
255
|
participantKind: participantKindSchema,
|
|
255
256
|
participantId: z.string().min(1),
|
|
257
|
+
deliveryPartition: deliveryPartitionRouteSchema.nullable().default(null),
|
|
256
258
|
});
|
|
257
259
|
export type CapabilityScope = z.infer<typeof capabilityScopeSchema>;
|
|
258
260
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Opaque gateway-routing coordinate resolved by the server.
|
|
5
|
+
*
|
|
6
|
+
* Clients echo this on the WebSocket upgrade; gateways still authenticate the
|
|
7
|
+
* credential and recompute ownership, so it is routing metadata rather than an
|
|
8
|
+
* authorization claim.
|
|
9
|
+
*/
|
|
10
|
+
export const deliveryPartitionRouteSchema = z.object({
|
|
11
|
+
index: z.number().int().nonnegative(),
|
|
12
|
+
count: z.number().int().min(2),
|
|
13
|
+
}).refine((route) => route.index < route.count, {
|
|
14
|
+
message: 'delivery partition index must be less than count',
|
|
15
|
+
path: ['index'],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type DeliveryPartitionRoute = z.infer<typeof deliveryPartitionRouteSchema>;
|
package/src/auth/identity.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type { Logger } from '../logger.js';
|
|
|
33
33
|
import type { AuthCredentialSource } from '../auth/credentialSource.js';
|
|
34
34
|
import type { CredentialProvider } from './credentialResult.js';
|
|
35
35
|
import { resolveApiKeyValue, resolveBootstrapBaseUrl } from './apiKey.js';
|
|
36
|
+
import type { DeliveryPartitionRoute } from './deliveryPartition.js';
|
|
36
37
|
|
|
37
38
|
export interface IdentityResolveInput {
|
|
38
39
|
readonly options: {
|
|
@@ -69,6 +70,7 @@ export interface ResolvedIdentity {
|
|
|
69
70
|
readonly capabilityToken: string | undefined;
|
|
70
71
|
readonly syncGroups: readonly string[] | undefined;
|
|
71
72
|
readonly participantKind: ParticipantKind;
|
|
73
|
+
readonly deliveryPartition: DeliveryPartitionRoute | null;
|
|
72
74
|
/** Set only on the hosted-cloud path; the caller keeps it to stop refreshes on shutdown. */
|
|
73
75
|
readonly refreshScheduler: RefreshScheduler | null;
|
|
74
76
|
}
|
|
@@ -192,6 +194,7 @@ export async function resolveParticipantIdentity(
|
|
|
192
194
|
capabilityToken: cred.getBearer,
|
|
193
195
|
syncGroups: options.syncGroups,
|
|
194
196
|
participantKind: kind,
|
|
197
|
+
deliveryPartition: null,
|
|
195
198
|
refreshScheduler: null,
|
|
196
199
|
};
|
|
197
200
|
}
|
|
@@ -242,6 +245,7 @@ async function resolveViaIdentity(
|
|
|
242
245
|
capabilityToken: bearer,
|
|
243
246
|
syncGroups: mergedSyncGroups,
|
|
244
247
|
participantKind: identity.participantKind,
|
|
248
|
+
deliveryPartition: identity.deliveryPartition,
|
|
245
249
|
refreshScheduler: null,
|
|
246
250
|
};
|
|
247
251
|
}
|
|
@@ -325,6 +329,7 @@ async function resolveHosted(input: HostedInput): Promise<ResolvedIdentity> {
|
|
|
325
329
|
capabilityToken: exchange.token,
|
|
326
330
|
syncGroups: exchange.scope.syncGroups,
|
|
327
331
|
participantKind: input.kind,
|
|
332
|
+
deliveryPartition: exchange.scope.deliveryPartition,
|
|
328
333
|
refreshScheduler,
|
|
329
334
|
};
|
|
330
335
|
}
|
package/src/auth/schemas.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from './capability.js';
|
|
8
8
|
import { AbloAuthenticationError } from '../errors.js';
|
|
9
9
|
import { authTokenSchema } from './token.js';
|
|
10
|
+
import { deliveryPartitionRouteSchema } from './deliveryPartition.js';
|
|
10
11
|
|
|
11
12
|
// Not a second enum. The auth responses carry the same participant vocabulary
|
|
12
13
|
// the coordination plane parses, so they validate against the same schema — a
|
|
@@ -24,6 +25,7 @@ export const IdentityResolveResponseSchema = z.object({
|
|
|
24
25
|
branchId: z.string().min(1).nullable().default(null),
|
|
25
26
|
branchRoot: z.boolean().default(false),
|
|
26
27
|
syncGroups: z.array(z.string()),
|
|
28
|
+
deliveryPartition: deliveryPartitionRouteSchema.nullable().default(null),
|
|
27
29
|
userMeta: z.record(z.string(), z.unknown()),
|
|
28
30
|
});
|
|
29
31
|
|
|
@@ -46,6 +48,7 @@ export const EphemeralKeyResponseSchema = z.object({
|
|
|
46
48
|
branchId: z.string().min(1).nullable().default(null),
|
|
47
49
|
branchRoot: z.boolean().default(false),
|
|
48
50
|
syncGroups: z.array(z.string()),
|
|
51
|
+
deliveryPartition: deliveryPartitionRouteSchema.nullable().default(null),
|
|
49
52
|
/** Effective operation grant stored on the credential. Empty ONLY for a
|
|
50
53
|
* control-plane-only session, which grants no data operations by design. */
|
|
51
54
|
operations: z.array(grantedOperationSchema),
|
package/src/auth/sessionMint.ts
CHANGED
|
@@ -98,6 +98,7 @@ export async function mintSession<S extends SchemaRecord>(
|
|
|
98
98
|
operations: res.operations,
|
|
99
99
|
participantKind: 'user',
|
|
100
100
|
participantId: res.participantId,
|
|
101
|
+
deliveryPartition: res.deliveryPartition,
|
|
101
102
|
},
|
|
102
103
|
userMeta: params.userMeta ?? { id: res.participantId },
|
|
103
104
|
};
|
package/src/branches.ts
CHANGED
|
@@ -87,6 +87,28 @@ export const branchParentCompatibilitySchema = z.enum([
|
|
|
87
87
|
]);
|
|
88
88
|
export type BranchParentCompatibility = z.infer<typeof branchParentCompatibilitySchema>;
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* The one ownership/storage vocabulary for durable branch data.
|
|
92
|
+
* Missing configuration is explicit (`unbound`), never inferred as hosted.
|
|
93
|
+
*/
|
|
94
|
+
export const branchStorageSchema = z.discriminatedUnion('kind', [
|
|
95
|
+
z.object({ kind: z.literal('unbound') }).strict(),
|
|
96
|
+
z.object({
|
|
97
|
+
kind: z.literal('customer'),
|
|
98
|
+
transport: z.enum(['direct', 'endpoint']),
|
|
99
|
+
status: z.enum(['unverified', 'active', 'rejected']),
|
|
100
|
+
}).strict(),
|
|
101
|
+
z.object({
|
|
102
|
+
kind: z.literal('internal'),
|
|
103
|
+
implementation: z.enum(['log', 'tables']),
|
|
104
|
+
}).strict(),
|
|
105
|
+
z.object({
|
|
106
|
+
kind: z.literal('blocked'),
|
|
107
|
+
reason: z.literal('orphaned_external_marker'),
|
|
108
|
+
}).strict(),
|
|
109
|
+
]);
|
|
110
|
+
export type BranchStorage = z.infer<typeof branchStorageSchema>;
|
|
111
|
+
|
|
90
112
|
export const branchStatusSchemaSummarySchema = z
|
|
91
113
|
.object({
|
|
92
114
|
active: z.boolean(),
|
|
@@ -101,8 +123,8 @@ export const branchStatusSchemaSummarySchema = z
|
|
|
101
123
|
|
|
102
124
|
export const branchStatusDataSourceSchema = z
|
|
103
125
|
.object({
|
|
104
|
-
|
|
105
|
-
status: z.enum(['unverified', 'active', 'rejected'])
|
|
126
|
+
connection: z.enum(['direct', 'endpoint']),
|
|
127
|
+
status: z.enum(['unverified', 'active', 'rejected']),
|
|
106
128
|
host: z.string().nullable(),
|
|
107
129
|
database: z.string().nullable(),
|
|
108
130
|
cursor: z.string().nullable(),
|
|
@@ -111,7 +133,8 @@ export const branchStatusDataSourceSchema = z
|
|
|
111
133
|
last_success_at: z.string().nullable(),
|
|
112
134
|
last_error: z.string().nullable(),
|
|
113
135
|
})
|
|
114
|
-
.strict()
|
|
136
|
+
.strict()
|
|
137
|
+
.nullable();
|
|
115
138
|
|
|
116
139
|
export const branchStatusBlockerSchema = z
|
|
117
140
|
.object({
|
|
@@ -127,6 +150,7 @@ export const branchStatusResponseSchema = z
|
|
|
127
150
|
branch: branchResponseSchema,
|
|
128
151
|
ready: z.boolean(),
|
|
129
152
|
schema: branchStatusSchemaSummarySchema,
|
|
153
|
+
storage: branchStorageSchema,
|
|
130
154
|
data_source: branchStatusDataSourceSchema,
|
|
131
155
|
blockers: z.array(branchStatusBlockerSchema).readonly(),
|
|
132
156
|
})
|
package/src/errorCodes.ts
CHANGED
|
@@ -39,7 +39,7 @@ import { z } from 'zod';
|
|
|
39
39
|
* error documentation and returned on the `Ablo-Version` response header, so a
|
|
40
40
|
* consumer can detect when its expected contract has drifted from the server's.
|
|
41
41
|
*/
|
|
42
|
-
export const ERROR_CONTRACT_VERSION = '2026-
|
|
42
|
+
export const ERROR_CONTRACT_VERSION = '2026-08-03';
|
|
43
43
|
|
|
44
44
|
/** A coarse grouping of error codes, used to organize metrics and documentation. */
|
|
45
45
|
export type ErrorCategory =
|
|
@@ -319,6 +319,18 @@ export const ERROR_CODES = {
|
|
|
319
319
|
false,
|
|
320
320
|
'This deployment does not accept direct connection-string data sources. Register a signed Data Source endpoint instead.'
|
|
321
321
|
),
|
|
322
|
+
source_connector_localhost_required: wire(
|
|
323
|
+
'validation',
|
|
324
|
+
400,
|
|
325
|
+
false,
|
|
326
|
+
'A connector-only development Data Source must use a localhost descriptor. Use `ablo dev --local`; use the ordinary signed HTTPS endpoint registration for a deployed handler.'
|
|
327
|
+
),
|
|
328
|
+
source_connector_unauthenticated: wire(
|
|
329
|
+
'auth',
|
|
330
|
+
401,
|
|
331
|
+
false,
|
|
332
|
+
'The localhost Data Source connector could not authenticate. Rerun `ablo dev --local` so it mints and uses a fresh branch-bound secret key.'
|
|
333
|
+
),
|
|
322
334
|
|
|
323
335
|
// ── permission / capability (403) ──────────────────────────────────
|
|
324
336
|
capability_scope_denied: wire(
|
|
@@ -339,12 +351,6 @@ export const ERROR_CODES = {
|
|
|
339
351
|
false,
|
|
340
352
|
'This capability cannot be used — it is unknown, revoked, or expired. Request a fresh grant.'
|
|
341
353
|
),
|
|
342
|
-
test_database_not_registered: wire(
|
|
343
|
-
'permission',
|
|
344
|
-
403,
|
|
345
|
-
false,
|
|
346
|
-
'Test mode requires a registered dev database for this org — run `npx ablo init` to register one for your test key.'
|
|
347
|
-
),
|
|
348
354
|
tenant_routing_failed: wire(
|
|
349
355
|
'server',
|
|
350
356
|
500,
|
|
@@ -373,7 +379,7 @@ export const ERROR_CODES = {
|
|
|
373
379
|
'permission',
|
|
374
380
|
403,
|
|
375
381
|
false,
|
|
376
|
-
"The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to. Use a publicly resolvable
|
|
382
|
+
"The database host resolves to a private, loopback, or link-local address, which Ablo's servers will not connect to directly. Use a publicly resolvable direct endpoint, private networking, or `ablo dev --local` with a signed Data Source reverse channel."
|
|
377
383
|
),
|
|
378
384
|
connected_database_unreachable: wire(
|
|
379
385
|
'tenant',
|
|
@@ -699,6 +705,18 @@ export const ERROR_CODES = {
|
|
|
699
705
|
false,
|
|
700
706
|
'The participant is not allowed to execute this mutator.'
|
|
701
707
|
),
|
|
708
|
+
source_connector_requires_secret_key: wire(
|
|
709
|
+
'permission',
|
|
710
|
+
403,
|
|
711
|
+
false,
|
|
712
|
+
'A Data Source reverse channel must authenticate with the branch-bound secret (`sk_`) key created by `ablo dev`; browser, ephemeral, restricted, and management keys cannot serve a database.'
|
|
713
|
+
),
|
|
714
|
+
source_connector_production_not_enabled: wire(
|
|
715
|
+
'permission',
|
|
716
|
+
403,
|
|
717
|
+
false,
|
|
718
|
+
'This production Data Source has not opted into reverse-channel transport. `ablo dev --local` is for child branches; use a direct production endpoint or explicitly enable the supported production reverse-channel route.'
|
|
719
|
+
),
|
|
702
720
|
|
|
703
721
|
// ── not found (404) ────────────────────────────────────────────────
|
|
704
722
|
entity_not_found: wire(
|
|
@@ -723,7 +741,13 @@ export const ERROR_CODES = {
|
|
|
723
741
|
'not_found',
|
|
724
742
|
404,
|
|
725
743
|
false,
|
|
726
|
-
'
|
|
744
|
+
'This branch is not connected to your database yet. Run `ablo connect` for a cloud-reachable direct Postgres endpoint, or `ablo dev --local` to register and serve a localhost Data Source, then retry.'
|
|
745
|
+
),
|
|
746
|
+
source_connector_no_source_registered: wire(
|
|
747
|
+
'not_found',
|
|
748
|
+
404,
|
|
749
|
+
false,
|
|
750
|
+
'The branch has no endpoint Data Source for this connector to serve. Run `ablo dev --local` with the current CLI, which registers the connector-only source before opening the socket.'
|
|
727
751
|
),
|
|
728
752
|
task_id_missing: wire(
|
|
729
753
|
'server',
|
|
@@ -978,7 +1002,55 @@ export const ERROR_CODES = {
|
|
|
978
1002
|
'transport',
|
|
979
1003
|
503,
|
|
980
1004
|
true,
|
|
981
|
-
'A network error occurred while talking to the data source.
|
|
1005
|
+
'A network error occurred while talking to the data source. For localhost mode, keep `ablo dev --local` running and check its connector output; for a direct source, check database connectivity and retry.'
|
|
1006
|
+
),
|
|
1007
|
+
source_request_failed: wire(
|
|
1008
|
+
'transport',
|
|
1009
|
+
502,
|
|
1010
|
+
true,
|
|
1011
|
+
'The signed Data Source returned a failure without a more specific code. Inspect the `ablo dev --local` process or deployed endpoint logs, then retry with the same idempotency key.'
|
|
1012
|
+
),
|
|
1013
|
+
source_connector_not_attached: wire(
|
|
1014
|
+
'transport',
|
|
1015
|
+
503,
|
|
1016
|
+
true,
|
|
1017
|
+
'This branch uses localhost connector-only storage, but no connector is attached. Start or restart `ablo dev --local` and keep that process running.'
|
|
1018
|
+
),
|
|
1019
|
+
source_connector_timeout: wire(
|
|
1020
|
+
'transport',
|
|
1021
|
+
504,
|
|
1022
|
+
true,
|
|
1023
|
+
'The local Data Source handler did not answer before the connector deadline. Check the local Postgres connection and handler logs; the same idempotent request may be retried.'
|
|
1024
|
+
),
|
|
1025
|
+
source_connector_send_failed: wire(
|
|
1026
|
+
'transport',
|
|
1027
|
+
502,
|
|
1028
|
+
true,
|
|
1029
|
+
'The connector socket closed while Ablo was forwarding a signed Data Source request. Keep `ablo dev --local` running; Ablo retries after the connector reconnects.'
|
|
1030
|
+
),
|
|
1031
|
+
source_connector_disconnected: wire(
|
|
1032
|
+
'transport',
|
|
1033
|
+
503,
|
|
1034
|
+
true,
|
|
1035
|
+
'The localhost Data Source connector disconnected with a request in flight. The connector reconnects automatically; retry after it reports ready.'
|
|
1036
|
+
),
|
|
1037
|
+
source_connector_superseded: wire(
|
|
1038
|
+
'transport',
|
|
1039
|
+
503,
|
|
1040
|
+
true,
|
|
1041
|
+
'A newer localhost Data Source connector replaced this one. Stop duplicate `ablo dev --local` processes and use the newest process; in-flight requests are safe to retry.'
|
|
1042
|
+
),
|
|
1043
|
+
source_connector_shutdown: wire(
|
|
1044
|
+
'transport',
|
|
1045
|
+
503,
|
|
1046
|
+
true,
|
|
1047
|
+
'The Ablo service shut down the connector while a request was in flight. The connector reconnects automatically after the service returns.'
|
|
1048
|
+
),
|
|
1049
|
+
source_connector_protocol_error: wire(
|
|
1050
|
+
'transport',
|
|
1051
|
+
400,
|
|
1052
|
+
false,
|
|
1053
|
+
'The CLI and Ablo service disagreed on the Data Source connector frame protocol, or a malformed frame arrived. Upgrade the Ablo CLI and SDK together, then restart `ablo dev --local`.'
|
|
982
1054
|
),
|
|
983
1055
|
source_unreachable: wire(
|
|
984
1056
|
'transport',
|
|
@@ -990,7 +1062,19 @@ export const ERROR_CODES = {
|
|
|
990
1062
|
'transport',
|
|
991
1063
|
503,
|
|
992
1064
|
false,
|
|
993
|
-
"
|
|
1065
|
+
"This branch's database connection is not ready. Check its credentials and configuration; Ablo will not send reads or writes anywhere else."
|
|
1066
|
+
),
|
|
1067
|
+
source_connector_handler_error: wire(
|
|
1068
|
+
'server',
|
|
1069
|
+
500,
|
|
1070
|
+
true,
|
|
1071
|
+
'The local signed Data Source handler threw while processing a request. Read the `ablo dev --local` error immediately above it, fix the database or adapter failure, and retry.'
|
|
1072
|
+
),
|
|
1073
|
+
source_connector_internal_error: wire(
|
|
1074
|
+
'server',
|
|
1075
|
+
500,
|
|
1076
|
+
true,
|
|
1077
|
+
'Ablo could not establish the Data Source reverse channel because of an internal service failure. Retry; if it persists, report the request id.'
|
|
994
1078
|
),
|
|
995
1079
|
identity_network_error: wire(
|
|
996
1080
|
'transport',
|
|
@@ -1046,6 +1130,12 @@ export const ERROR_CODES = {
|
|
|
1046
1130
|
true,
|
|
1047
1131
|
'The server is at connection capacity. Retry shortly — transient and not specific to your credentials.'
|
|
1048
1132
|
),
|
|
1133
|
+
delivery_partition_mismatch: wire(
|
|
1134
|
+
'transport',
|
|
1135
|
+
503,
|
|
1136
|
+
true,
|
|
1137
|
+
'The connection reached a gateway that does not own its delivery partition. Retry through the cell router.'
|
|
1138
|
+
),
|
|
1049
1139
|
fetch_unavailable: client(
|
|
1050
1140
|
'transport',
|
|
1051
1141
|
'This environment provides no `fetch` implementation, so HTTP requests cannot be made. Run on a platform with `fetch` (Node 18+, modern browsers) or supply a polyfill.'
|
|
@@ -1134,7 +1224,7 @@ export const ERROR_CODES = {
|
|
|
1134
1224
|
),
|
|
1135
1225
|
db_identity_mismatch: client(
|
|
1136
1226
|
'client',
|
|
1137
|
-
'The local database
|
|
1227
|
+
'The local database is connected to a different organization, project, or branch.'
|
|
1138
1228
|
),
|
|
1139
1229
|
db_cleanup_failed: client(
|
|
1140
1230
|
'client',
|
|
@@ -1240,6 +1330,12 @@ export const ERROR_CODES = {
|
|
|
1240
1330
|
false,
|
|
1241
1331
|
"The request targeted a project the caller's key is not scoped to."
|
|
1242
1332
|
),
|
|
1333
|
+
branch_scope_denied: wire(
|
|
1334
|
+
'permission',
|
|
1335
|
+
403,
|
|
1336
|
+
false,
|
|
1337
|
+
"The request targeted a branch the caller's key is not scoped to."
|
|
1338
|
+
),
|
|
1243
1339
|
project_slug_taken: wire(
|
|
1244
1340
|
'validation',
|
|
1245
1341
|
409,
|
|
@@ -1294,19 +1390,19 @@ export const ERROR_CODES = {
|
|
|
1294
1390
|
'tenant',
|
|
1295
1391
|
400,
|
|
1296
1392
|
false,
|
|
1297
|
-
"This model
|
|
1393
|
+
"This model gets its tenant identity from the connected Data Source, but that connection is not ready for scoped reads yet. Ablo refused the read to prevent data from crossing tenant boundaries. Connect the branch and retry; for internally logged models, use `by: 'column'` or `by: 'parent'`."
|
|
1298
1394
|
),
|
|
1299
1395
|
user_scope_not_enforced: wire(
|
|
1300
1396
|
'tenant',
|
|
1301
1397
|
400,
|
|
1302
1398
|
false,
|
|
1303
|
-
"Rows in this model belong to one person rather than
|
|
1399
|
+
"Rows in this model belong to one person rather than the whole organization, but the current read source does not carry the owner identity needed to enforce that boundary. Ablo returned nothing to prevent one member from seeing another member's private records. Use a read source that preserves owner identity, or a credential that acts for the organization."
|
|
1304
1400
|
),
|
|
1305
1401
|
model_not_provisioned: wire(
|
|
1306
1402
|
'tenant',
|
|
1307
1403
|
409,
|
|
1308
1404
|
false,
|
|
1309
|
-
"This model is in the
|
|
1405
|
+
"This model is in the branch's registered schema, but its table does not exist in the connected database yet. `ablo push` records the model but does not change a customer's database. Apply the table migration in your database, then retry the read."
|
|
1310
1406
|
),
|
|
1311
1407
|
schema_table_invalid: wire('schema', 500, false, "The model's table identifier is invalid."),
|
|
1312
1408
|
schema_scope_invalid: wire(
|
|
@@ -1480,7 +1576,7 @@ export const ERROR_CODES = {
|
|
|
1480
1576
|
'not_found',
|
|
1481
1577
|
404,
|
|
1482
1578
|
false,
|
|
1483
|
-
'No claim of yours exists with the given id. It was released,
|
|
1579
|
+
'No claim of yours exists with the given id. It was released, expired, or belongs to a different branch.'
|
|
1484
1580
|
),
|
|
1485
1581
|
invalid_participant_kind: wire(
|
|
1486
1582
|
'validation',
|
|
@@ -1547,7 +1643,7 @@ export const ERROR_CODES = {
|
|
|
1547
1643
|
'conflict',
|
|
1548
1644
|
409,
|
|
1549
1645
|
false,
|
|
1550
|
-
'
|
|
1646
|
+
'This branch has live replicated rows for a mapped model that disappeared. Declare a rename or an explicit drop/reset before advancing the schema.'
|
|
1551
1647
|
),
|
|
1552
1648
|
} as const satisfies Record<string, ErrorCodeSpec>;
|
|
1553
1649
|
|
package/src/footprint.ts
CHANGED
|
@@ -51,25 +51,14 @@ export interface FootprintArtifact {
|
|
|
51
51
|
|
|
52
52
|
// ── The names, for the code that creates and reads these objects ────────────
|
|
53
53
|
|
|
54
|
-
/**
|
|
55
|
-
*
|
|
56
|
-
* publication declares what streams, and several readers are meant to share one
|
|
57
|
-
* (`publication_names` is a per-stream option, not slot state).
|
|
58
|
-
*/
|
|
54
|
+
/** Prefix used to derive a branch-scoped publication name. The exact unsuffixed
|
|
55
|
+
* name is retained only so `ablo connect scan` can identify an old installation. */
|
|
59
56
|
export const ABLO_PUBLICATION = 'ablo_publication';
|
|
60
|
-
/**
|
|
61
|
-
* The replication slot that holds Ablo's position in the write-ahead log.
|
|
62
|
-
*
|
|
63
|
-
* Constant, and it should not be — a slot stores ONE position, so two
|
|
64
|
-
* connections sharing this name compete for the same marker. It is why a
|
|
65
|
-
* database can be connected to one plane at a time. ADR 0020 derives it per
|
|
66
|
-
* connection; {@link isValidReplicationSlotName} is the check a derived name
|
|
67
|
-
* has to pass.
|
|
68
|
-
*/
|
|
57
|
+
/** Prefix used to derive the branch-scoped replication slot. */
|
|
69
58
|
export const ABLO_REPLICATION_SLOT = 'ablo_slot';
|
|
70
|
-
/**
|
|
59
|
+
/** Prefix used to derive the branch-scoped least-privilege replication login. */
|
|
71
60
|
export const ABLO_REPLICATION_ROLE = 'ablo_replicator';
|
|
72
|
-
/**
|
|
61
|
+
/** Prefix used to derive the branch-scoped login used only for row writes. */
|
|
73
62
|
export const ABLO_WRITE_ROLE = 'ablo_writer';
|
|
74
63
|
/** The bookkeeping table that makes a retried write land once. */
|
|
75
64
|
export const ABLO_IDEMPOTENCY_TABLE = 'ablo_idempotency';
|
|
@@ -90,15 +79,15 @@ export function isValidReplicationSlotName(name: string): boolean {
|
|
|
90
79
|
|
|
91
80
|
// ── Per-connection names (ADR 0020) ─────────────────────────────────────────
|
|
92
81
|
|
|
93
|
-
/** The immutable branch coordinates
|
|
94
|
-
export interface
|
|
82
|
+
/** The immutable branch coordinates that identify one customer Data Source. */
|
|
83
|
+
export interface DataSourceIdentity {
|
|
95
84
|
readonly organizationId: string;
|
|
96
85
|
readonly branchId: string;
|
|
97
86
|
/** Omitted for the organization-default project. */
|
|
98
87
|
readonly projectId?: string;
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
/** The names one connection owns. Nothing here is shared with another
|
|
90
|
+
/** The names one connection owns. Nothing here is shared with another Data Source. */
|
|
102
91
|
export interface FootprintNames {
|
|
103
92
|
readonly slot: string;
|
|
104
93
|
readonly publication: string;
|
|
@@ -114,7 +103,7 @@ export interface FootprintNames {
|
|
|
114
103
|
const SUFFIX_LENGTH = 16;
|
|
115
104
|
|
|
116
105
|
/**
|
|
117
|
-
* FNV-1a over the
|
|
106
|
+
* FNV-1a over the Data Source identity, in hex.
|
|
118
107
|
*
|
|
119
108
|
* Deliberately not a cryptographic hash: this module is imported by the CLI,
|
|
120
109
|
* which runs in environments without `node:crypto` guaranteed, and the property
|
|
@@ -123,11 +112,14 @@ const SUFFIX_LENGTH = 16;
|
|
|
123
112
|
* over different seeds give the sixteen hex characters, which is ample for the
|
|
124
113
|
* handful of planes any one database is ever connected to.
|
|
125
114
|
*/
|
|
126
|
-
function
|
|
115
|
+
function dataSourceDigest(identity: DataSourceIdentity): string {
|
|
127
116
|
// The organization-default project may be omitted or repeated as the
|
|
128
117
|
// organization id; both spell the same branch coordinates.
|
|
129
|
-
const project =
|
|
130
|
-
|
|
118
|
+
const project =
|
|
119
|
+
identity.projectId === identity.organizationId ? '' : (identity.projectId ?? '');
|
|
120
|
+
// Delimit the coordinates. Concatenation alone makes structurally different
|
|
121
|
+
// planes such as ("ab", "c") and ("a", "bc") hash the same input.
|
|
122
|
+
const key = [identity.organizationId, project, identity.branchId].join('\0');
|
|
131
123
|
|
|
132
124
|
const round = (seed: number): string => {
|
|
133
125
|
let hash = seed;
|
|
@@ -146,16 +138,15 @@ function planeDigest(plane: FootprintPlane): string {
|
|
|
146
138
|
* The objects this connection owns, named so no other connection can claim them.
|
|
147
139
|
*
|
|
148
140
|
* A slot stores ONE position, so two connections sharing a name compete for the
|
|
149
|
-
* same marker and Postgres reports nothing
|
|
150
|
-
* replace made a database connectable to one plane at a time. The publication
|
|
141
|
+
* same marker and Postgres reports nothing. The publication
|
|
151
142
|
* and roles are derived from the same digest so a database's footprint reads as
|
|
152
143
|
* one set per connection rather than a mix of shared and private objects.
|
|
153
144
|
*
|
|
154
145
|
* Stable: the same plane always derives the same names, so re-running setup is a
|
|
155
146
|
* no-op rather than a second installation.
|
|
156
147
|
*/
|
|
157
|
-
export function footprintNamesFor(
|
|
158
|
-
const suffix =
|
|
148
|
+
export function footprintNamesFor(identity: DataSourceIdentity): FootprintNames {
|
|
149
|
+
const suffix = dataSourceDigest(identity);
|
|
159
150
|
const names: FootprintNames = {
|
|
160
151
|
slot: `${ABLO_REPLICATION_SLOT}_${suffix}`,
|
|
161
152
|
publication: `${ABLO_PUBLICATION}_${suffix}`,
|
package/src/pricing.ts
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* rather than billed.
|
|
24
24
|
*
|
|
25
25
|
* The allowance a tier advertises is DERIVED, never stored: "the floor covers
|
|
26
|
-
*
|
|
26
|
+
* 10M operations" is {@link opsForUsd} applied to the floor. Storing it
|
|
27
27
|
* alongside the floor is how a published price silently stops matching the
|
|
28
28
|
* invoice, because raising one number does not fail anything that pins the
|
|
29
29
|
* other.
|
|
@@ -47,7 +47,7 @@ export type { MeterEvent, PlanTier, RateBracket };
|
|
|
47
47
|
* could observe the difference. It is emitted into the generated pricing
|
|
48
48
|
* documentation so a stale copy is identifiable on sight.
|
|
49
49
|
*/
|
|
50
|
-
export const PRICING_VERSION = '2026-07-
|
|
50
|
+
export const PRICING_VERSION = '2026-07-31';
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Resolve a stored plan string (`stripe_subscription.plan`) to a tier.
|
|
@@ -82,12 +82,40 @@ export const METER_EVENT_AXIS: Record<MeterEvent, BillableAxis | null> = {
|
|
|
82
82
|
|
|
83
83
|
/** How each meter reads on an invoice line and in the pricing table. */
|
|
84
84
|
export const METER_EVENT_LABEL: Record<MeterEvent, string> = {
|
|
85
|
-
'api.commit_ops': '
|
|
85
|
+
'api.commit_ops': 'writes',
|
|
86
86
|
'api.model_reads': 'model reads',
|
|
87
87
|
'api.claim_creates': 'claim creates',
|
|
88
88
|
'api.bootstraps': 'bootstraps',
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* What each meter counts, in the words a customer is quoted. It lives beside
|
|
93
|
+
* the label because a meter's name and its definition are one fact: a page that
|
|
94
|
+
* renamed the line without restating what it counts would be describing a
|
|
95
|
+
* different meter.
|
|
96
|
+
*
|
|
97
|
+
* These say **write**, never *commit*. A commit is the protocol's atomic batch
|
|
98
|
+
* and it belongs in the protocol reference; the surface a customer buys against
|
|
99
|
+
* is `create` / `update` / `delete`, which the front door calls writing. Pricing
|
|
100
|
+
* copy is read by people who have never opened the protocol reference, and a
|
|
101
|
+
* word they have not met cannot clarify what they are paying for.
|
|
102
|
+
*
|
|
103
|
+
* All three read as "someone did something", because they are one price and a
|
|
104
|
+
* reader who has to hold three different shapes in mind will assume there are
|
|
105
|
+
* three different charges. Each line then answers the question that particular
|
|
106
|
+
* meter provokes: whether batching is cheaper, whether cached reads count, and
|
|
107
|
+
* whether holding a claim costs more than taking one.
|
|
108
|
+
*/
|
|
109
|
+
export const METER_EVENT_COUNTS: Record<MeterEvent, string> = {
|
|
110
|
+
'api.commit_ops':
|
|
111
|
+
'One every time someone changes something. Adding, editing, and removing all count the same, and changing 500 things is 500 operations however they are sent.',
|
|
112
|
+
'api.model_reads':
|
|
113
|
+
'One every time someone reads something the engine answers. Reads a client already has locally never reach us and never count.',
|
|
114
|
+
'api.claim_creates':
|
|
115
|
+
'One every time someone takes ownership of a row so others wait their turn. Holding it and releasing it are free.',
|
|
116
|
+
'api.bootstraps': 'Recorded, never charged. Connecting a client is free.',
|
|
117
|
+
};
|
|
118
|
+
|
|
91
119
|
/** The meters a customer is charged for, in declaration order. */
|
|
92
120
|
export const BILLABLE_METER_EVENTS: readonly MeterEvent[] = (
|
|
93
121
|
meterEventSchema.options as readonly MeterEvent[]
|
|
@@ -185,8 +213,8 @@ export const planDefinitionSchema = z.object({
|
|
|
185
213
|
hardCapOps: z.number().int().positive().nullable(),
|
|
186
214
|
/**
|
|
187
215
|
* A hard daily stop, which is a burst guard rather than an allowance. A month
|
|
188
|
-
* of operations is a comfortable month of building and about
|
|
189
|
-
*
|
|
216
|
+
* of operations is a comfortable month of building and about a minute of one
|
|
217
|
+
* agent at full rate, so a monthly cap alone lets a runaway loop erase the
|
|
190
218
|
* whole month before anyone notices. The daily figure is what turns that into
|
|
191
219
|
* a floor on how fast the month can be spent. `null` on metered tiers, which
|
|
192
220
|
* are billed rather than stopped.
|
|
@@ -214,8 +242,8 @@ export const PLANS = z
|
|
|
214
242
|
label: 'Free',
|
|
215
243
|
summary: 'Build against the real engine without a card.',
|
|
216
244
|
monthlyMinimumUsd: 0,
|
|
217
|
-
hardCapOps:
|
|
218
|
-
hardCapOpsPerDay:
|
|
245
|
+
hardCapOps: 100_000,
|
|
246
|
+
hardCapOpsPerDay: 10_000,
|
|
219
247
|
storageGib: 1,
|
|
220
248
|
maxConcurrentConnections: 25,
|
|
221
249
|
contractPriced: false,
|
|
@@ -225,7 +253,7 @@ export const PLANS = z
|
|
|
225
253
|
tier: 'scale',
|
|
226
254
|
label: 'Scale',
|
|
227
255
|
summary: 'Production traffic, metered above a monthly floor.',
|
|
228
|
-
monthlyMinimumUsd:
|
|
256
|
+
monthlyMinimumUsd: 20,
|
|
229
257
|
hardCapOps: null,
|
|
230
258
|
hardCapOpsPerDay: null,
|
|
231
259
|
storageGib: 50,
|