@abloatai/ablo 0.29.3 → 0.30.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 +31 -0
- package/README.md +3 -2
- package/dist/BaseSyncedStore.js +3 -1
- package/dist/Database.js +29 -2
- package/dist/SyncClient.d.ts +5 -3
- package/dist/SyncClient.js +8 -4
- package/dist/ai-sdk/coordinatedTool.d.ts +0 -1
- package/dist/ai-sdk/coordinatedTool.js +0 -1
- package/dist/ai-sdk/coordinationContext.js +1 -5
- package/dist/cli.cjs +377 -84
- package/dist/client/Ablo.d.ts +1 -1
- package/dist/client/Ablo.js +24 -36
- package/dist/client/auth.d.ts +8 -18
- package/dist/client/auth.js +16 -96
- package/dist/client/createModelProxy.d.ts +16 -11
- package/dist/client/createModelProxy.js +162 -20
- package/dist/client/httpClient.d.ts +1 -1
- package/dist/client/httpTransport.js +263 -89
- package/dist/client/options.d.ts +0 -15
- package/dist/client/resourceTypes.d.ts +13 -26
- package/dist/client/schemaConfig.js +5 -0
- package/dist/client/writeOptionsSchema.d.ts +1 -0
- package/dist/client/writeOptionsSchema.js +11 -2
- package/dist/client/wsMutationExecutor.d.ts +3 -5
- package/dist/coordination/schema.d.ts +17 -13
- package/dist/coordination/schema.js +36 -10
- package/dist/errorCodes.d.ts +9 -4
- package/dist/errorCodes.js +10 -5
- package/dist/errors.js +4 -9
- package/dist/index.d.ts +4 -0
- package/dist/interfaces/index.d.ts +31 -19
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +4 -1
- package/dist/schema/schema.d.ts +13 -0
- package/dist/schema/select.js +7 -0
- package/dist/schema/syncDeltaRow.d.ts +31 -13
- package/dist/schema/syncDeltaRow.js +33 -15
- package/dist/schema/tenancy.d.ts +52 -16
- package/dist/schema/tenancy.js +86 -25
- package/dist/server/commit.d.ts +8 -24
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/readConfig.d.ts +10 -0
- package/dist/source/adapter.d.ts +26 -15
- package/dist/source/adapter.js +5 -3
- package/dist/source/adapters/drizzle.js +35 -9
- package/dist/source/adapters/kysely.d.ts +33 -61
- package/dist/source/adapters/kysely.js +170 -177
- package/dist/source/adapters/kyselyMutationCore.d.ts +76 -0
- package/dist/source/adapters/kyselyMutationCore.js +125 -0
- package/dist/source/adapters/memory.d.ts +1 -1
- package/dist/source/adapters/memory.js +24 -10
- package/dist/source/adapters/prisma.js +35 -8
- package/dist/source/conformance.d.ts +26 -28
- package/dist/source/conformance.js +142 -69
- package/dist/source/contract.d.ts +46 -4
- package/dist/source/contract.js +66 -5
- package/dist/source/factory.d.ts +3 -3
- package/dist/source/factory.js +24 -3
- package/dist/source/idempotency.d.ts +46 -0
- package/dist/source/idempotency.js +129 -0
- package/dist/source/index.d.ts +6 -4
- package/dist/source/index.js +5 -3
- package/dist/source/migrations.d.ts +9 -5
- package/dist/source/migrations.js +39 -6
- package/dist/source/types.d.ts +88 -14
- package/dist/source/types.js +16 -0
- package/dist/surface.d.ts +1 -1
- package/dist/surface.js +0 -1
- package/dist/sync/BootstrapFetcher.js +7 -0
- package/dist/sync/SyncWebSocket.d.ts +5 -27
- package/dist/sync/awaitClaimGrant.d.ts +6 -0
- package/dist/sync/awaitClaimGrant.js +12 -2
- package/dist/sync/commitFrames.d.ts +2 -4
- package/dist/sync/commitFrames.js +1 -0
- package/dist/sync/createClaimStream.d.ts +1 -1
- package/dist/sync/createClaimStream.js +13 -15
- package/dist/sync/deltaPipeline.d.ts +1 -1
- package/dist/sync/deltaPipeline.js +1 -1
- package/dist/sync/participants.d.ts +2 -3
- package/dist/sync/participants.js +2 -2
- package/dist/sync/wsFrameHandlers.js +35 -15
- package/dist/testing/mocks/MockMutationExecutor.d.ts +21 -0
- package/dist/testing/mocks/MockMutationExecutor.js +53 -1
- package/dist/transactions/TransactionQueue.d.ts +61 -9
- package/dist/transactions/TransactionQueue.js +502 -114
- package/dist/transactions/commitEnvelope.d.ts +4 -0
- package/dist/transactions/commitEnvelope.js +25 -0
- package/dist/transactions/commitPayload.d.ts +23 -5
- package/dist/transactions/commitPayload.js +3 -0
- package/dist/transactions/deltaConfirmation.d.ts +2 -1
- package/dist/transactions/deltaConfirmation.js +22 -4
- package/dist/transactions/durableWriteStore.d.ts +8 -1
- package/dist/transactions/httpCommitEnvelope.d.ts +3 -1
- package/dist/transactions/httpCommitEnvelope.js +30 -0
- package/dist/transactions/replayValidation.d.ts +4 -0
- package/dist/transactions/replayValidation.js +6 -0
- package/dist/types/streams.d.ts +29 -17
- package/dist/wire/commit.d.ts +671 -0
- package/dist/wire/commit.js +300 -0
- package/dist/wire/delta.d.ts +4 -0
- package/dist/wire/delta.js +9 -0
- package/dist/wire/frames.d.ts +17 -45
- package/dist/wire/index.d.ts +2 -0
- package/dist/wire/index.js +4 -0
- package/docs/agent-messaging.md +4 -5
- package/docs/agents.md +1 -2
- package/docs/api-keys.md +1 -2
- package/docs/client-behavior.md +4 -6
- package/docs/coordination.md +64 -9
- package/docs/data-sources.md +4 -7
- package/docs/integration-guide.md +6 -8
- package/docs/migration.md +0 -3
- package/docs/quickstart.md +7 -8
- package/docs/schema-contract.md +7 -8
- package/package.json +4 -4
- package/dist/client/registerDataSource.d.ts +0 -19
- package/dist/client/registerDataSource.js +0 -66
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* so tests can drive the delta-confirmation flow that depends on those ids.
|
|
7
7
|
*/
|
|
8
8
|
import type { MutationExecutor, MutationOperation, MutationOptions, CommitResult } from '../../interfaces/index.js';
|
|
9
|
+
import type { StaleNotification } from '../../coordination/schema.js';
|
|
9
10
|
export interface CapturedMutation {
|
|
10
11
|
method: string;
|
|
11
12
|
modelName?: string;
|
|
@@ -23,6 +24,14 @@ export interface MockMutationExecutorOptions {
|
|
|
23
24
|
shouldSucceed?: boolean;
|
|
24
25
|
/** A delay applied before each call resolves, in milliseconds, to simulate a slow network. */
|
|
25
26
|
latencyMs?: number;
|
|
27
|
+
/** Optional explicit receipt status. Omission preserves legacy confirmed semantics. */
|
|
28
|
+
status?: CommitResult['status'];
|
|
29
|
+
/** Optional server-issued source/WAL correlation returned with queued receipts. */
|
|
30
|
+
correlationId?: string;
|
|
31
|
+
/** Optional stale-context notifications returned with each commit. */
|
|
32
|
+
notifications?: StaleNotification[];
|
|
33
|
+
/** Optional zero-row target ids returned with each commit. */
|
|
34
|
+
missingIds?: string[];
|
|
26
35
|
}
|
|
27
36
|
export declare class MockMutationExecutor implements MutationExecutor {
|
|
28
37
|
/** Every captured call, in the order it was made. Assertions read from this list. */
|
|
@@ -31,6 +40,10 @@ export declare class MockMutationExecutor implements MutationExecutor {
|
|
|
31
40
|
private _syncId;
|
|
32
41
|
private _shouldSucceed;
|
|
33
42
|
private _latencyMs;
|
|
43
|
+
private _status;
|
|
44
|
+
private _correlationId;
|
|
45
|
+
private _notifications;
|
|
46
|
+
private _missingIds;
|
|
34
47
|
/** Per-method failure overrides: method name → error */
|
|
35
48
|
private _failureOverrides;
|
|
36
49
|
/** Per-method response overrides */
|
|
@@ -40,6 +53,14 @@ export declare class MockMutationExecutor implements MutationExecutor {
|
|
|
40
53
|
get currentSyncId(): number;
|
|
41
54
|
/** Sets the next sync id the executor will return. */
|
|
42
55
|
setSyncId(id: number): void;
|
|
56
|
+
/** Sets the settlement status returned by subsequent commit calls. */
|
|
57
|
+
setStatus(status: CommitResult['status']): void;
|
|
58
|
+
/** Sets the opaque source/WAL correlation returned by subsequent commits. */
|
|
59
|
+
setCorrelationId(correlationId: string | undefined): void;
|
|
60
|
+
/** Sets stale-context notifications returned by subsequent commit calls. */
|
|
61
|
+
setNotifications(notifications: StaleNotification[] | undefined): void;
|
|
62
|
+
/** Sets zero-row target ids returned by subsequent commit calls. */
|
|
63
|
+
setMissingIds(missingIds: string[] | undefined): void;
|
|
43
64
|
/** Makes every mutation reject. Pass an error to control what is thrown. */
|
|
44
65
|
failAll(error?: Error): void;
|
|
45
66
|
/** Restores the default where mutations succeed, clearing any failure overrides. */
|
|
@@ -13,6 +13,10 @@ export class MockMutationExecutor {
|
|
|
13
13
|
_syncId;
|
|
14
14
|
_shouldSucceed;
|
|
15
15
|
_latencyMs;
|
|
16
|
+
_status;
|
|
17
|
+
_correlationId;
|
|
18
|
+
_notifications;
|
|
19
|
+
_missingIds;
|
|
16
20
|
/** Per-method failure overrides: method name → error */
|
|
17
21
|
_failureOverrides = new Map();
|
|
18
22
|
/** Per-method response overrides */
|
|
@@ -21,6 +25,10 @@ export class MockMutationExecutor {
|
|
|
21
25
|
this._syncId = options.initialSyncId ?? 1;
|
|
22
26
|
this._shouldSucceed = options.shouldSucceed ?? true;
|
|
23
27
|
this._latencyMs = options.latencyMs ?? 0;
|
|
28
|
+
this._status = options.status;
|
|
29
|
+
this._correlationId = options.correlationId;
|
|
30
|
+
this._notifications = options.notifications;
|
|
31
|
+
this._missingIds = options.missingIds;
|
|
24
32
|
}
|
|
25
33
|
// ─────────────────────────────────────────────
|
|
26
34
|
// Test control API
|
|
@@ -33,6 +41,22 @@ export class MockMutationExecutor {
|
|
|
33
41
|
setSyncId(id) {
|
|
34
42
|
this._syncId = id;
|
|
35
43
|
}
|
|
44
|
+
/** Sets the settlement status returned by subsequent commit calls. */
|
|
45
|
+
setStatus(status) {
|
|
46
|
+
this._status = status;
|
|
47
|
+
}
|
|
48
|
+
/** Sets the opaque source/WAL correlation returned by subsequent commits. */
|
|
49
|
+
setCorrelationId(correlationId) {
|
|
50
|
+
this._correlationId = correlationId;
|
|
51
|
+
}
|
|
52
|
+
/** Sets stale-context notifications returned by subsequent commit calls. */
|
|
53
|
+
setNotifications(notifications) {
|
|
54
|
+
this._notifications = notifications;
|
|
55
|
+
}
|
|
56
|
+
/** Sets zero-row target ids returned by subsequent commit calls. */
|
|
57
|
+
setMissingIds(missingIds) {
|
|
58
|
+
this._missingIds = missingIds;
|
|
59
|
+
}
|
|
36
60
|
/** Makes every mutation reject. Pass an error to control what is thrown. */
|
|
37
61
|
failAll(error) {
|
|
38
62
|
this._shouldSucceed = false;
|
|
@@ -67,6 +91,10 @@ export class MockMutationExecutor {
|
|
|
67
91
|
this._syncId = options?.initialSyncId ?? 1;
|
|
68
92
|
this._shouldSucceed = options?.shouldSucceed ?? true;
|
|
69
93
|
this._latencyMs = options?.latencyMs ?? 0;
|
|
94
|
+
this._status = options?.status;
|
|
95
|
+
this._correlationId = options?.correlationId;
|
|
96
|
+
this._notifications = options?.notifications;
|
|
97
|
+
this._missingIds = options?.missingIds;
|
|
70
98
|
this._failureOverrides.clear();
|
|
71
99
|
this._responseOverrides.clear();
|
|
72
100
|
}
|
|
@@ -78,7 +106,31 @@ export class MockMutationExecutor {
|
|
|
78
106
|
await this._maybeDelay();
|
|
79
107
|
this._maybeThrow('commit');
|
|
80
108
|
const syncId = this._syncId++;
|
|
81
|
-
|
|
109
|
+
if (this._status === 'queued') {
|
|
110
|
+
if (!this._correlationId) {
|
|
111
|
+
throw new Error('Mock queued commits require a correlationId');
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
lastSyncId: 0,
|
|
115
|
+
status: 'queued',
|
|
116
|
+
correlationId: this._correlationId,
|
|
117
|
+
...(this._notifications ? { notifications: this._notifications } : {}),
|
|
118
|
+
...(this._missingIds ? { missingIds: this._missingIds } : {}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const evidence = {
|
|
122
|
+
...(this._notifications ? { notifications: this._notifications } : {}),
|
|
123
|
+
...(this._missingIds ? { missingIds: this._missingIds } : {}),
|
|
124
|
+
};
|
|
125
|
+
if (this._status === 'confirmed') {
|
|
126
|
+
return {
|
|
127
|
+
lastSyncId: syncId,
|
|
128
|
+
status: 'confirmed',
|
|
129
|
+
...(this._correlationId ? { correlationId: this._correlationId } : {}),
|
|
130
|
+
...evidence,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return { lastSyncId: syncId, ...evidence };
|
|
82
134
|
}
|
|
83
135
|
async executeCreate(modelName, id, input, clientMutationId) {
|
|
84
136
|
this._capture('executeCreate', { modelName, modelId: id, input, clientMutationId });
|
|
@@ -45,10 +45,12 @@ interface CommitTransaction {
|
|
|
45
45
|
causedByTaskId?: string | null;
|
|
46
46
|
/** Read dependencies for the whole batch, forwarded to the executor so the server can detect stale-context writes. */
|
|
47
47
|
reads?: ReadDependency[] | null;
|
|
48
|
-
status: 'pending' | 'executing' | 'completed' | 'failed';
|
|
48
|
+
status: 'pending' | 'executing' | 'awaiting_delta' | 'completed' | 'failed';
|
|
49
49
|
createdAt: number;
|
|
50
50
|
attempts: number;
|
|
51
51
|
lastSyncId?: number;
|
|
52
|
+
/** Opaque customer-side batch ledger key returned by a queued receipt. */
|
|
53
|
+
correlationId?: string;
|
|
52
54
|
error?: Error;
|
|
53
55
|
sealedAt: number;
|
|
54
56
|
sequence: number;
|
|
@@ -113,6 +115,20 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
113
115
|
private deferredDeletesByCreate;
|
|
114
116
|
private commitLane;
|
|
115
117
|
private commitStore;
|
|
118
|
+
/**
|
|
119
|
+
* Small race buffer for authoritative echoes that arrive before the queued
|
|
120
|
+
* mutation receipt. The forward and WAL stream are independent channels, so
|
|
121
|
+
* either can win without changing the settlement result.
|
|
122
|
+
*/
|
|
123
|
+
private recentDeltaCorrelations;
|
|
124
|
+
/**
|
|
125
|
+
* Client-facing confirmation deadlines for source-forwarded writes. These
|
|
126
|
+
* timers settle only the caller waiting for `confirmed`; the accepted write
|
|
127
|
+
* remains in `awaiting_delta`, with its durable envelope intact, until the
|
|
128
|
+
* authoritative WAL echo arrives.
|
|
129
|
+
*/
|
|
130
|
+
private replicationLagTimeouts;
|
|
131
|
+
private replicationLagErrors;
|
|
116
132
|
private commitProcessing;
|
|
117
133
|
private lastCommitSequence;
|
|
118
134
|
private durableReplayBlock;
|
|
@@ -126,6 +142,18 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
126
142
|
private computePriorityScore;
|
|
127
143
|
private ensureDerivedFields;
|
|
128
144
|
private entityKey;
|
|
145
|
+
/** Collision-safe receipt target identity across models sharing a row id. */
|
|
146
|
+
private receiptTargetKey;
|
|
147
|
+
/**
|
|
148
|
+
* Relates stale notifications back to write targets without assuming the
|
|
149
|
+
* server's canonical model name uses the same spelling as the public schema
|
|
150
|
+
* key (`Task` versus `tasks`). Exact `(model,id)` wins; a globally unique id
|
|
151
|
+
* is the compatibility fallback. An ambiguous same-id cross-model mismatch
|
|
152
|
+
* is deliberately left unclassified, so it cannot falsely settle a queued
|
|
153
|
+
* write. A notification with no write-target id (or an explicit group) is a
|
|
154
|
+
* declared-read conflict and holds the whole batch.
|
|
155
|
+
*/
|
|
156
|
+
private classifyReceiptNotifications;
|
|
129
157
|
private resolveConfirmation;
|
|
130
158
|
private takeUnsentCreateForModel;
|
|
131
159
|
private cancelUnsentCreateForDelete;
|
|
@@ -137,6 +165,8 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
137
165
|
private executingCount;
|
|
138
166
|
private optimisticUpdates;
|
|
139
167
|
private commitNotifications;
|
|
168
|
+
/** Zero-row targets returned on a successful atomic commit receipt. */
|
|
169
|
+
private commitMissingIds;
|
|
140
170
|
private readonly deltaConfirmation;
|
|
141
171
|
private isConnectedFn;
|
|
142
172
|
private commitOfflineGraceTimer;
|
|
@@ -170,8 +200,25 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
170
200
|
* callers never send a separately reconstructed payload after sealing.
|
|
171
201
|
*/
|
|
172
202
|
private sealDurableCommit;
|
|
173
|
-
/** Best-effort cleanup after a definitive
|
|
203
|
+
/** Best-effort cleanup after a definitive rejection, confirmed ack, or echo. */
|
|
174
204
|
private removeDurableCommit;
|
|
205
|
+
/**
|
|
206
|
+
* Upgrade a sealed request with permanent connected-source acceptance before
|
|
207
|
+
* exposing the queued receipt. This is not completion: the envelope remains
|
|
208
|
+
* until the matching WAL correlation arrives. The upgrade only makes a
|
|
209
|
+
* crash/restart safe after the hosted 24-hour replay window has elapsed.
|
|
210
|
+
*/
|
|
211
|
+
private persistDurableCommitAcceptance;
|
|
212
|
+
/** Parse an untrusted/custom executor receipt without making ambiguity fatal. */
|
|
213
|
+
private parseMutationCommitResult;
|
|
214
|
+
private clearReplicationLagState;
|
|
215
|
+
/**
|
|
216
|
+
* Bounds the public `wait: 'confirmed'` promise without changing the
|
|
217
|
+
* accepted write's lifecycle. A lag timeout is not a rejection from the
|
|
218
|
+
* source database, so it must never emit `transaction:failed`, roll back
|
|
219
|
+
* optimistic state, or remove the durable replay envelope.
|
|
220
|
+
*/
|
|
221
|
+
private scheduleReplicationLagTimeout;
|
|
175
222
|
/**
|
|
176
223
|
* Takes either one complete retry envelope or a fresh batch. A retry waits
|
|
177
224
|
* until every original member has re-entered the queue, preventing an
|
|
@@ -333,13 +380,15 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
333
380
|
* greatly reduces batch latency.
|
|
334
381
|
*/
|
|
335
382
|
private processBatch;
|
|
383
|
+
private rememberDeltaCorrelation;
|
|
384
|
+
private queuedCommitEchoSyncId;
|
|
385
|
+
private queuedCommitMatchesCorrelation;
|
|
386
|
+
private completeQueuedCommit;
|
|
336
387
|
/**
|
|
337
|
-
* Confirms
|
|
338
|
-
*
|
|
339
|
-
* {@link DeltaConfirmationTracker} (`./deltaConfirmation.js`).
|
|
340
|
-
* @param syncId - The sync id of the received delta.
|
|
388
|
+
* Confirms awaiting writes. Hosted/anomaly receipts keep their sync-id
|
|
389
|
+
* threshold semantics; queued forwards require the exact server correlation.
|
|
341
390
|
*/
|
|
342
|
-
onDeltaReceived(syncId: number): void;
|
|
391
|
+
onDeltaReceived(syncId: number, _transactionId?: string, correlationId?: string): void;
|
|
343
392
|
private scheduleDeltaConfirmationTimeout;
|
|
344
393
|
/**
|
|
345
394
|
* Resolves once the given transaction is confirmed and rejects if it fails.
|
|
@@ -353,8 +402,10 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
353
402
|
* `ablo.commits.create()` path. The caller supplies the operations; the queue
|
|
354
403
|
* only retries on reconnect and de-duplicates, and does not apply the change
|
|
355
404
|
* optimistically or reorder for foreign keys. A duplicate `clientTxId`
|
|
356
|
-
* already in flight is ignored
|
|
357
|
-
*
|
|
405
|
+
* already in flight is ignored. After an accepted write exceeds its
|
|
406
|
+
* replication-confirmation deadline, an explicit same-key retry reuses the
|
|
407
|
+
* exact sealed envelope as an idempotent status probe; the server's
|
|
408
|
+
* `mutation_log` still prevents a second logical write.
|
|
358
409
|
*/
|
|
359
410
|
enqueueCommit(clientTxId: string, operations: CommitTransaction['operations'], options?: {
|
|
360
411
|
causedByTaskId?: string | null;
|
|
@@ -377,6 +428,7 @@ export declare class TransactionQueue extends EventEmitter {
|
|
|
377
428
|
waitForCommitReceipt(clientTxId: string): Promise<{
|
|
378
429
|
lastSyncId: number;
|
|
379
430
|
notifications?: StaleNotification[];
|
|
431
|
+
missingIds?: string[];
|
|
380
432
|
}>;
|
|
381
433
|
private isReorderPayload;
|
|
382
434
|
/**
|