@abloatai/transaction 0.48.0 → 0.49.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/CONVENTIONS.md +7 -7
- package/README.md +1 -1
- package/dist/ai-sdk/modelTools.d.ts +1 -1
- package/dist/ai-sdk/modelTools.js +1 -1
- package/dist/ai-sdk/updateTool.d.ts +2 -2
- package/dist/ai-sdk/updateTool.d.ts.map +1 -1
- package/dist/auth/bootstrapScope.d.ts +1 -1
- package/dist/auth/capability.d.ts +32 -10
- package/dist/auth/capability.d.ts.map +1 -1
- package/dist/auth/capability.js +9 -3
- package/dist/auth/capability.js.map +1 -1
- package/dist/auth/capabilityLifecycle.d.ts +2 -2
- package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
- package/dist/auth/capabilityLifecycle.js +3 -2
- package/dist/auth/capabilityLifecycle.js.map +1 -1
- package/dist/auth/identity.d.ts +3 -0
- package/dist/auth/identity.d.ts.map +1 -1
- package/dist/auth/identity.js +13 -1
- package/dist/auth/identity.js.map +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/schemas.d.ts +18 -1
- package/dist/auth/schemas.d.ts.map +1 -1
- package/dist/auth/schemas.js +3 -1
- package/dist/auth/schemas.js.map +1 -1
- package/dist/auth/sessionMint.d.ts.map +1 -1
- package/dist/auth/sessionMint.js +0 -1
- package/dist/auth/sessionMint.js.map +1 -1
- package/dist/commitRecordRuntime.d.ts +30 -0
- package/dist/commitRecordRuntime.d.ts.map +1 -0
- package/dist/commitRecordRuntime.js +188 -0
- package/dist/commitRecordRuntime.js.map +1 -0
- package/dist/coordination/index.d.ts +3 -3
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +2 -2
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/locator.d.ts +5 -0
- package/dist/coordination/locator.d.ts.map +1 -1
- package/dist/coordination/locator.js +12 -0
- package/dist/coordination/locator.js.map +1 -1
- package/dist/coordination/schema.d.ts +205 -15
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +88 -47
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/targetConflict.js +1 -1
- package/dist/durableWrites.d.ts +3 -3
- package/dist/durableWrites.d.ts.map +1 -1
- package/dist/durableWrites.js +1 -1
- package/dist/errorCodes.d.ts +20 -1
- package/dist/errorCodes.d.ts.map +1 -1
- package/dist/errorCodes.js +44 -10
- package/dist/errorCodes.js.map +1 -1
- package/dist/errorObservation.d.ts +82 -0
- package/dist/errorObservation.d.ts.map +1 -0
- package/dist/errorObservation.js +101 -0
- package/dist/errorObservation.js.map +1 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/read-set.d.ts +8 -0
- package/dist/internal/read-set.d.ts.map +1 -0
- package/dist/internal/read-set.js +7 -0
- package/dist/internal/read-set.js.map +1 -0
- package/dist/logger.d.ts +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +1 -1
- package/dist/readSetContext.d.ts +54 -0
- package/dist/readSetContext.d.ts.map +1 -0
- package/dist/readSetContext.js +98 -0
- package/dist/readSetContext.js.map +1 -0
- package/dist/resources/functionalUpdate.d.ts +13 -3
- package/dist/resources/functionalUpdate.d.ts.map +1 -1
- package/dist/resources/functionalUpdate.js.map +1 -1
- package/dist/resources/httpResources.d.ts +23 -7
- package/dist/resources/httpResources.d.ts.map +1 -1
- package/dist/resources/modelOperations.d.ts +2 -2
- package/dist/resources/modelOperations.js +1 -1
- package/dist/resources/mutationOptions.d.ts +1 -1
- package/dist/resources/mutationOptions.js +1 -1
- package/dist/resources/where.d.ts +1 -1
- package/dist/resources/where.js +1 -1
- package/dist/resources/writeOptionsSchema.d.ts +39 -4
- package/dist/resources/writeOptionsSchema.d.ts.map +1 -1
- package/dist/resources/writeOptionsSchema.js +12 -6
- package/dist/resources/writeOptionsSchema.js.map +1 -1
- package/dist/schema/index.js +1 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/openapi.d.ts.map +1 -1
- package/dist/schema/openapi.js +41 -7
- package/dist/schema/openapi.js.map +1 -1
- package/dist/server/commit.d.ts +7 -1
- package/dist/server/commit.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/source/adapters/kysely.d.ts.map +1 -1
- package/dist/source/adapters/kysely.js +11 -4
- package/dist/source/adapters/kysely.js.map +1 -1
- package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -1
- package/dist/source/adapters/kyselyMutationCore.js +14 -2
- package/dist/source/adapters/kyselyMutationCore.js.map +1 -1
- package/dist/source/contract.d.ts +2 -0
- package/dist/source/contract.d.ts.map +1 -1
- package/dist/source/types.d.ts +1 -1
- package/dist/testing/fixtures/httpResponses.d.ts +30 -1
- package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
- package/dist/testing/fixtures/httpResponses.js +48 -1
- package/dist/testing/fixtures/httpResponses.js.map +1 -1
- package/dist/transactionLayer.d.ts +1 -1
- package/dist/transactions/{settlement → confirmation}/commitEnvelope.d.ts +4 -2
- package/dist/transactions/confirmation/commitEnvelope.d.ts.map +1 -0
- package/dist/transactions/{settlement → confirmation}/commitEnvelope.js +8 -3
- package/dist/transactions/confirmation/commitEnvelope.js.map +1 -0
- package/dist/transactions/confirmation/httpCommitEnvelope.d.ts.map +1 -0
- package/dist/transactions/confirmation/httpCommitEnvelope.js.map +1 -0
- package/dist/transactions/confirmation/idempotencyKey.d.ts.map +1 -0
- package/dist/transactions/confirmation/idempotencyKey.js.map +1 -0
- package/dist/transactions/{settlement → confirmation}/pendingWrite.d.ts +3 -2
- package/dist/transactions/confirmation/pendingWrite.d.ts.map +1 -0
- package/dist/transactions/confirmation/pendingWrite.js.map +1 -0
- package/dist/transport/commitFrames.d.ts +1 -0
- package/dist/transport/commitFrames.d.ts.map +1 -1
- package/dist/transport/commitFrames.js +8 -7
- package/dist/transport/commitFrames.js.map +1 -1
- package/dist/transport/connectionManager.d.ts +1 -1
- package/dist/transport/connectionManager.js +1 -1
- package/dist/transport/httpClient.d.ts +23 -9
- package/dist/transport/httpClient.d.ts.map +1 -1
- package/dist/transport/httpClient.js +134 -15
- package/dist/transport/httpClient.js.map +1 -1
- package/dist/transport/httpOptions.d.ts +9 -1
- package/dist/transport/httpOptions.d.ts.map +1 -1
- package/dist/transport/httpTransport.d.ts +3 -0
- package/dist/transport/httpTransport.d.ts.map +1 -1
- package/dist/transport/httpTransport.js +109 -22
- package/dist/transport/httpTransport.js.map +1 -1
- package/dist/transport/wsFrameHandlers.d.ts +3 -1
- package/dist/transport/wsFrameHandlers.d.ts.map +1 -1
- package/dist/transport/wsFrameHandlers.js +38 -17
- package/dist/transport/wsFrameHandlers.js.map +1 -1
- package/dist/transport/wsTransport.d.ts +3 -0
- package/dist/transport/wsTransport.d.ts.map +1 -1
- package/dist/transport/wsTransport.js +32 -1
- package/dist/transport/wsTransport.js.map +1 -1
- package/dist/utils/json.d.ts +1 -1
- package/dist/utils/json.js +1 -1
- package/dist/wire/accountResponses.d.ts +1 -1
- package/dist/wire/auth.d.ts +2 -2
- package/dist/wire/commit.d.ts +1652 -213
- package/dist/wire/commit.d.ts.map +1 -1
- package/dist/wire/commit.js +196 -132
- package/dist/wire/commit.js.map +1 -1
- package/dist/wire/delta.d.ts +1 -1
- package/dist/wire/errorEnvelope.d.ts +1 -0
- package/dist/wire/errorEnvelope.d.ts.map +1 -1
- package/dist/wire/errorEnvelope.js +1 -0
- package/dist/wire/errorEnvelope.js.map +1 -1
- package/dist/wire/feedCursor.d.ts +1 -1
- package/dist/wire/feedCursor.js +1 -1
- package/dist/wire/feedEvent.d.ts +3 -3
- package/dist/wire/frames.d.ts +8 -4
- package/dist/wire/frames.d.ts.map +1 -1
- package/dist/wire/frames.js +6 -3
- package/dist/wire/frames.js.map +1 -1
- package/dist/wire/inboundFrames.d.ts +44 -4
- package/dist/wire/inboundFrames.d.ts.map +1 -1
- package/dist/wire/index.d.ts +6 -4
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +6 -5
- package/dist/wire/index.js.map +1 -1
- package/dist/wire/modelMutations.d.ts +35 -0
- package/dist/wire/modelMutations.d.ts.map +1 -1
- package/dist/wire/modelMutations.js +9 -2
- package/dist/wire/modelMutations.js.map +1 -1
- package/dist/wire/modelResponses.d.ts +12 -1
- package/dist/wire/modelResponses.d.ts.map +1 -1
- package/dist/wire/modelResponses.js +12 -1
- package/dist/wire/modelResponses.js.map +1 -1
- package/package.json +12 -2
- package/src/ai-sdk/modelTools.ts +1 -1
- package/src/ai-sdk/updateTool.ts +2 -2
- package/src/auth/bootstrapScope.ts +1 -1
- package/src/auth/capability.ts +13 -4
- package/src/auth/capabilityLifecycle.ts +3 -2
- package/src/auth/identity.ts +16 -1
- package/src/auth/index.ts +2 -0
- package/src/auth/schemas.ts +3 -0
- package/src/auth/sessionMint.ts +0 -1
- package/src/commitRecordRuntime.ts +245 -0
- package/src/coordination/index.ts +24 -1
- package/src/coordination/locator.ts +17 -0
- package/src/coordination/schema.ts +114 -49
- package/src/coordination/targetConflict.ts +1 -1
- package/src/durableWrites.ts +3 -3
- package/src/errorCodes.ts +107 -11
- package/src/errorObservation.ts +121 -0
- package/src/errors.ts +5 -1
- package/src/index.ts +8 -1
- package/src/internal/read-set.ts +18 -0
- package/src/logger.ts +1 -1
- package/src/observability.ts +1 -1
- package/src/readSetContext.ts +164 -0
- package/src/resources/functionalUpdate.ts +16 -4
- package/src/resources/httpResources.ts +27 -8
- package/src/resources/modelOperations.ts +2 -2
- package/src/resources/mutationOptions.ts +1 -1
- package/src/resources/where.ts +1 -1
- package/src/resources/writeOptionsSchema.ts +30 -12
- package/src/schema/index.ts +1 -1
- package/src/schema/openapi.ts +48 -7
- package/src/server/commit.ts +7 -1
- package/src/server/index.ts +5 -1
- package/src/source/adapters/kysely.ts +12 -4
- package/src/source/adapters/kyselyMutationCore.ts +14 -2
- package/src/source/types.ts +1 -1
- package/src/testing/fixtures/httpResponses.ts +60 -1
- package/src/transactionLayer.ts +1 -1
- package/src/transactions/{settlement → confirmation}/commitEnvelope.ts +13 -3
- package/src/transport/commitFrames.ts +7 -5
- package/src/transport/connectionManager.ts +1 -1
- package/src/transport/httpClient.ts +256 -29
- package/src/transport/httpOptions.ts +9 -1
- package/src/transport/httpTransport.ts +139 -27
- package/src/transport/wsFrameHandlers.ts +46 -8
- package/src/transport/wsTransport.ts +42 -1
- package/src/utils/json.ts +1 -1
- package/src/wire/commit.ts +248 -147
- package/src/wire/errorEnvelope.ts +1 -0
- package/src/wire/feedCursor.ts +1 -1
- package/src/wire/frames.ts +9 -4
- package/src/wire/index.ts +38 -5
- package/src/wire/modelMutations.ts +16 -2
- package/src/wire/modelResponses.ts +14 -1
- package/dist/transactions/settlement/commitEnvelope.d.ts.map +0 -1
- package/dist/transactions/settlement/commitEnvelope.js.map +0 -1
- package/dist/transactions/settlement/httpCommitEnvelope.d.ts.map +0 -1
- package/dist/transactions/settlement/httpCommitEnvelope.js.map +0 -1
- package/dist/transactions/settlement/idempotencyKey.d.ts.map +0 -1
- package/dist/transactions/settlement/idempotencyKey.js.map +0 -1
- package/dist/transactions/settlement/pendingWrite.d.ts.map +0 -1
- package/dist/transactions/settlement/pendingWrite.js.map +0 -1
- /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.d.ts +0 -0
- /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.js +0 -0
- /package/dist/transactions/{settlement → confirmation}/idempotencyKey.d.ts +0 -0
- /package/dist/transactions/{settlement → confirmation}/idempotencyKey.js +0 -0
- /package/dist/transactions/{settlement → confirmation}/pendingWrite.js +0 -0
- /package/src/transactions/{settlement → confirmation}/httpCommitEnvelope.ts +0 -0
- /package/src/transactions/{settlement → confirmation}/idempotencyKey.ts +0 -0
- /package/src/transactions/{settlement → confirmation}/pendingWrite.ts +0 -0
|
@@ -15,14 +15,19 @@ import type { ClaimHeartbeatReply, ClaimState } from '../wire/claims.js';
|
|
|
15
15
|
import type {
|
|
16
16
|
ClientCommitReceipt,
|
|
17
17
|
CommitWait,
|
|
18
|
+
CommitRecord,
|
|
19
|
+
CommitRecordList,
|
|
20
|
+
CommitRecordListOptions,
|
|
21
|
+
CommitRecordWhere,
|
|
18
22
|
} from '../wire/commit.js';
|
|
19
23
|
import type { LogListResponse, LogQuery } from '../wire/feedEvent.js';
|
|
20
|
-
|
|
24
|
+
import type { ModelListEvidence } from '../wire/modelResponses.js';
|
|
25
|
+
// Re-exported, not redeclared. `wire/commit.ts` owns the commit-status vocabulary
|
|
21
26
|
// and derives the waitable subset from it; this module serves that name to SDK
|
|
22
27
|
// consumers. Restating the subset here as its own union produced a type that
|
|
23
28
|
// matched the canonical one only by both happening to list the same two
|
|
24
29
|
// strings — and would have silently disagreed with the runtime `wait`
|
|
25
|
-
// validator the moment a third
|
|
30
|
+
// validator the moment a third commit status existed.
|
|
26
31
|
export type { CommitWait };
|
|
27
32
|
// `ModelTarget` (the `model` and `id` locator) and `ModelClaim` (the resolved
|
|
28
33
|
// claim view) are defined in `../coordination/schema`, derived from a single
|
|
@@ -39,7 +44,7 @@ import type { SyncGroupInput } from '../schema/roles.js';
|
|
|
39
44
|
import type {
|
|
40
45
|
CapabilityCan,
|
|
41
46
|
CapabilityOperation,
|
|
42
|
-
|
|
47
|
+
EffectiveAuthority,
|
|
43
48
|
} from '../auth/capability.js';
|
|
44
49
|
import type {
|
|
45
50
|
Claim,
|
|
@@ -48,7 +53,7 @@ import type {
|
|
|
48
53
|
Duration,
|
|
49
54
|
HeldClaim,
|
|
50
55
|
} from '../types/streams.js';
|
|
51
|
-
import type { ModelUpdater,
|
|
56
|
+
import type { ModelUpdater, FunctionalUpdateOptions } from './functionalUpdate.js';
|
|
52
57
|
import type {
|
|
53
58
|
ClaimOptions,
|
|
54
59
|
ClaimAttemptEvent,
|
|
@@ -93,6 +98,12 @@ export interface HttpTransportRead<T = Record<string, unknown>> {
|
|
|
93
98
|
readonly claims: readonly ModelClaim[];
|
|
94
99
|
}
|
|
95
100
|
|
|
101
|
+
/** @internal Exact collection envelope retained until the typed facade captures evidence. */
|
|
102
|
+
export interface HttpTransportList<T = Record<string, unknown>> {
|
|
103
|
+
readonly data: readonly T[];
|
|
104
|
+
readonly evidence?: readonly ModelListEvidence[];
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
export type IfClaimedPolicy = 'return' | 'fail';
|
|
97
108
|
|
|
98
109
|
export interface ClaimedOptions {
|
|
@@ -160,6 +171,8 @@ export interface CommitOperationInput {
|
|
|
160
171
|
readonly id?: string | null;
|
|
161
172
|
readonly data?: Record<string, unknown> | null;
|
|
162
173
|
readonly transactionId?: string | null;
|
|
174
|
+
/** Claim identity derived from a held claim; not an application id. */
|
|
175
|
+
readonly claimId?: string | null;
|
|
163
176
|
readonly readAt?: number | null;
|
|
164
177
|
readonly onStale?: OnStaleMode | null;
|
|
165
178
|
/** Fencing token (Option B) from the batch's claim handle; server-validated. */
|
|
@@ -207,6 +220,8 @@ export type CommitReceipt = ClientCommitReceipt;
|
|
|
207
220
|
|
|
208
221
|
export interface CommitResource {
|
|
209
222
|
create(options: CommitCreateOptions): Promise<CommitReceipt>;
|
|
223
|
+
get(options: { readonly id: string }): Promise<CommitRecord | null>;
|
|
224
|
+
list(options?: CommitRecordListOptions): Promise<CommitRecordList>;
|
|
210
225
|
}
|
|
211
226
|
|
|
212
227
|
export interface HttpLogListOptions
|
|
@@ -276,6 +291,10 @@ export interface ModelMutationOptions extends ClaimedOptions {
|
|
|
276
291
|
readonly idempotencyKey?: string | null;
|
|
277
292
|
readonly readAt?: number | null;
|
|
278
293
|
readonly onStale?: OnStaleMode | null;
|
|
294
|
+
/** Commit-lifetime read dependencies checked before this mutation lands. */
|
|
295
|
+
readonly reads?: readonly ReadDependency[] | null;
|
|
296
|
+
/** Persisted read dependencies registered by this mutation. */
|
|
297
|
+
readonly track?: readonly TrackDependency[] | null;
|
|
279
298
|
readonly claim?: Claim | ClaimOptions | null;
|
|
280
299
|
/** Fencing token (Option B) from the claim; server-validated at commit. */
|
|
281
300
|
readonly fenceToken?: number | null;
|
|
@@ -332,7 +351,7 @@ export interface HttpTransportModel<
|
|
|
332
351
|
* and `limit`. The typed public client always exposes `ablo.<model>.list`;
|
|
333
352
|
* this protocol shape is private transport machinery.
|
|
334
353
|
*/
|
|
335
|
-
list(options?: ServerReadOptions<T>): Promise<T
|
|
354
|
+
list(options?: ServerReadOptions<T>): Promise<HttpTransportList<T>>;
|
|
336
355
|
/**
|
|
337
356
|
* Creates a row and returns the confirmed server row, including framework
|
|
338
357
|
* defaults such as `createdAt` and `createdBy`. Matches the stateful client's
|
|
@@ -354,7 +373,7 @@ export interface HttpTransportModel<
|
|
|
354
373
|
update(
|
|
355
374
|
id: string,
|
|
356
375
|
updater: ModelUpdater<T>,
|
|
357
|
-
options?:
|
|
376
|
+
options?: FunctionalUpdateOptions,
|
|
358
377
|
): Promise<CommitReceipt | undefined>;
|
|
359
378
|
delete(params: ModelMutationOptions & { readonly id: string }): Promise<CommitReceipt>;
|
|
360
379
|
/**
|
|
@@ -475,7 +494,7 @@ export interface AbloSession {
|
|
|
475
494
|
organizationId: string;
|
|
476
495
|
/** The grant this token carries, on both axes — the same shape the key row
|
|
477
496
|
* stores and the gates enforce. */
|
|
478
|
-
scope:
|
|
497
|
+
scope: EffectiveAuthority;
|
|
479
498
|
userMeta: Record<string, unknown>;
|
|
480
499
|
}
|
|
481
500
|
|
|
@@ -492,7 +511,7 @@ export interface SessionRotation {
|
|
|
492
511
|
token: string;
|
|
493
512
|
expiresAt: string | null;
|
|
494
513
|
organizationId: string;
|
|
495
|
-
scope:
|
|
514
|
+
scope: EffectiveAuthority;
|
|
496
515
|
rotatedFrom: {
|
|
497
516
|
id: string;
|
|
498
517
|
expiresAt: string;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* caller passes to a read, a write, or a claim.
|
|
4
4
|
*
|
|
5
5
|
* These types describe the *change or the query being requested*, never a local
|
|
6
|
-
* copy of the rows it touches, so they sit in the
|
|
6
|
+
* copy of the rows it touches, so they sit in the commit core and are shared
|
|
7
7
|
* by every transport and every caller (ADR 0013 §4, ADR 0016). The factory that
|
|
8
8
|
* binds them to reactive model instances — `createModelProxy` — stays with the
|
|
9
9
|
* reactive consumer, along with `ModelOperations` and `ModelCollaboration`,
|
|
@@ -562,7 +562,7 @@ export interface ModelRetrieveParams extends ServerRetrieveOptions {
|
|
|
562
562
|
* Options shared by schema model writes.
|
|
563
563
|
*
|
|
564
564
|
* Reactive clients apply the row change optimistically before returning from
|
|
565
|
-
* the call. The returned promise has one stable
|
|
565
|
+
* the call. The returned promise has one stable confirmation contract across
|
|
566
566
|
* reactive and stateless clients: it resolves only after authoritative
|
|
567
567
|
* confirmation. Callers that need an earlier queued receipt use the lower-level
|
|
568
568
|
* `commits.create` resource instead.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Every field here defines, orders, settles, or authorises *the change itself*
|
|
5
5
|
* — request identity, commit disposition, fencing, and the premise it rests on.
|
|
6
|
-
* None of it touches a local copy of rows, so it belongs with the
|
|
6
|
+
* None of it touches a local copy of rows, so it belongs with the commit
|
|
7
7
|
* core rather than the reactive consumer (ADR 0013 §4, ADR 0016).
|
|
8
8
|
*/
|
|
9
9
|
|
package/src/resources/where.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* key — for example, fetching every block whose `sectionId` falls in a set of ids.
|
|
11
11
|
*
|
|
12
12
|
* These types describe the *request*, not any local copy of the rows it returns,
|
|
13
|
-
* so they live with the
|
|
13
|
+
* so they live with the commit core rather than the reactive consumer.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { z } from 'zod';
|
|
@@ -20,7 +20,15 @@ import { z } from 'zod';
|
|
|
20
20
|
import type { MutationOptions } from '../resources/mutationOptions.js';
|
|
21
21
|
import { AbloValidationError } from '../errors.js';
|
|
22
22
|
import { commitWaitSchema } from '../wire/commit.js';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
onStaleModeSchema,
|
|
25
|
+
MAX_READ_SET_ENTRIES,
|
|
26
|
+
readDependencyListSchema,
|
|
27
|
+
readSetWatermarkSchema,
|
|
28
|
+
readSetProjectionEntryCount,
|
|
29
|
+
trackDependencyListSchema,
|
|
30
|
+
} from '../coordination/schema.js';
|
|
31
|
+
import type { AssertExact } from '../types/assertExact.js';
|
|
24
32
|
|
|
25
33
|
// Re-exported, not redeclared. `coordination/schema.ts` owns this enum — it is
|
|
26
34
|
// what the wire schemas and the server validate against — while the published
|
|
@@ -38,7 +46,7 @@ export const writeOptionsSchema = z.object({
|
|
|
38
46
|
/** Resolve when queued locally (default) or once the server confirms. */
|
|
39
47
|
wait: commitWaitSchema.optional(),
|
|
40
48
|
/** Stale guard: the sync watermark the caller's reasoning was based on. */
|
|
41
|
-
readAt:
|
|
49
|
+
readAt: readSetWatermarkSchema.nullish(),
|
|
42
50
|
/** What the server does when the target moved past `readAt`. */
|
|
43
51
|
onStale: onStaleModeSchema.nullish(),
|
|
44
52
|
/** The held claim's fencing token (Option B), sourced from the claim handle
|
|
@@ -49,6 +57,15 @@ export const writeOptionsSchema = z.object({
|
|
|
49
57
|
/** The claim this write belongs to — either a claim id, or a live claim
|
|
50
58
|
* handle whose `release`/`revoke` functions are preserved untouched. */
|
|
51
59
|
claim: z.union([z.string(), z.looseObject({ id: z.string() })]).nullish(),
|
|
60
|
+
/** Low-level claim identity carried after a live handle is normalized. */
|
|
61
|
+
claimRef: z.union([z.string(), z.object({ id: z.string() })]).nullish(),
|
|
62
|
+
/** Commit-lifetime entries in the write's ReadSet. */
|
|
63
|
+
reads: readDependencyListSchema.nullish(),
|
|
64
|
+
/** Persisted entries projected from the write's ReadSet. */
|
|
65
|
+
track: trackDependencyListSchema.nullish(),
|
|
66
|
+
}).refine((value) => readSetProjectionEntryCount(value) <= MAX_READ_SET_ENTRIES, {
|
|
67
|
+
path: ['reads'],
|
|
68
|
+
message: `reads and track may contain at most ${MAX_READ_SET_ENTRIES} entries combined`,
|
|
52
69
|
});
|
|
53
70
|
|
|
54
71
|
export type WriteOptionsInput = z.infer<typeof writeOptionsSchema>;
|
|
@@ -78,14 +95,15 @@ export function assertWriteOptions(value: unknown, context?: string): void {
|
|
|
78
95
|
}
|
|
79
96
|
|
|
80
97
|
// ── Drift guard ──────────────────────────────────────────────────────────────
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
type
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
98
|
+
// `claim` is the one high-level handle normalized before MutationOptions. Every
|
|
99
|
+
// other key is the canonical interface itself. Compare keys exactly: mutual
|
|
100
|
+
// assignment between all-optional objects is vacuous and failed to catch three
|
|
101
|
+
// missing members in this schema.
|
|
102
|
+
type WriteOptionsContract = MutationOptions & {
|
|
103
|
+
readonly claim?: string | { readonly id: string } | null;
|
|
104
|
+
};
|
|
105
|
+
const _writeOptionsContractInSync: AssertExact<
|
|
106
|
+
keyof WriteOptionsInput,
|
|
107
|
+
keyof WriteOptionsContract
|
|
108
|
+
> = true;
|
|
91
109
|
void _writeOptionsContractInSync;
|
package/src/schema/index.ts
CHANGED
|
@@ -275,6 +275,6 @@ export {
|
|
|
275
275
|
|
|
276
276
|
// The OpenAPI generator describes the language-agnostic `/v1` surface — the
|
|
277
277
|
// primitive itself, which a Python or Go caller consumes with no TypeScript
|
|
278
|
-
// anywhere — so it lives in the
|
|
278
|
+
// anywhere — so it lives in the confirmation core (ADR 0016). Re-exported here so
|
|
279
279
|
// the `@abloatai/ablo/schema` import path keeps resolving.
|
|
280
280
|
export { abloOpenApi, schemaToOpenApi, type SchemaToOpenApiOptions } from './openapi.js';
|
package/src/schema/openapi.ts
CHANGED
|
@@ -29,7 +29,13 @@ import { ABLO_HOSTED_HTTP_BASE_URL } from '../auth/hostedEndpoints.js';
|
|
|
29
29
|
// one definition site, so the documented surface cannot drift from the
|
|
30
30
|
// enforced one.
|
|
31
31
|
import { z } from 'zod';
|
|
32
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
commitRequestSchema,
|
|
34
|
+
commitReceiptSchema,
|
|
35
|
+
commitRecordSchema,
|
|
36
|
+
commitRecordListSchema,
|
|
37
|
+
commitRecordWhereSchema,
|
|
38
|
+
} from '../wire/commit.js';
|
|
33
39
|
import {
|
|
34
40
|
claimRequestSchema,
|
|
35
41
|
claimHeartbeatRequestSchema,
|
|
@@ -154,7 +160,9 @@ const ABLO_OPERATION_IDS: Readonly<Record<string, string>> = {
|
|
|
154
160
|
'POST /v1/capabilities/{id}/rotate': 'rotateCapability',
|
|
155
161
|
'GET /v1/schema': 'getSchema',
|
|
156
162
|
'GET /v1/logs': 'listLogEntries',
|
|
163
|
+
'GET /v1/commits': 'listCommits',
|
|
157
164
|
'POST /v1/commits': 'commit',
|
|
165
|
+
'GET /v1/commits/{id}': 'getCommit',
|
|
158
166
|
};
|
|
159
167
|
|
|
160
168
|
function fieldSchema(f: FieldMeta): Json {
|
|
@@ -317,6 +325,11 @@ function queryParams(schema: z.ZodType): Json[] {
|
|
|
317
325
|
}));
|
|
318
326
|
}
|
|
319
327
|
|
|
328
|
+
const commitRecordListQuerySchema = commitRecordWhereSchema.safeExtend({
|
|
329
|
+
cursor: z.string().min(1).optional(),
|
|
330
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
331
|
+
});
|
|
332
|
+
|
|
320
333
|
/**
|
|
321
334
|
* Both release routes answer in one shape. `released` distinguishes "this call
|
|
322
335
|
* ended your lease" from "there was nothing of yours to end" — both success,
|
|
@@ -736,11 +749,9 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
736
749
|
tags: ['claims'],
|
|
737
750
|
summary: 'Heartbeat a claim by id — held or queued',
|
|
738
751
|
description:
|
|
739
|
-
'
|
|
740
|
-
'`
|
|
741
|
-
'
|
|
742
|
-
'`queued` means still in line, `held` means the grant landed, at which ' +
|
|
743
|
-
'point `GET /v1/claims/{claimId}` carries the fence token.',
|
|
752
|
+
'Keep a held or queued claim active. Branch on the returned status: ' +
|
|
753
|
+
'`queued` is still waiting and `held` has been granted. Retrieve the ' +
|
|
754
|
+
'claim after a grant before writing.',
|
|
744
755
|
parameters: [claimIdParam()],
|
|
745
756
|
requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
|
|
746
757
|
responses: {
|
|
@@ -849,6 +860,12 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
849
860
|
},
|
|
850
861
|
},
|
|
851
862
|
'/v1/commits': {
|
|
863
|
+
get: {
|
|
864
|
+
tags: ['commits'],
|
|
865
|
+
summary: 'List commit records',
|
|
866
|
+
parameters: queryParams(commitRecordListQuerySchema),
|
|
867
|
+
responses: { '200': jsonResp('Tenant-scoped commit records', derive(commitRecordListSchema, 'output')) },
|
|
868
|
+
},
|
|
852
869
|
post: {
|
|
853
870
|
tags: ['commits'],
|
|
854
871
|
summary: 'Commit a batch of operations atomically, and/or register durable premises',
|
|
@@ -857,6 +874,14 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
857
874
|
responses: { '200': commitReceipt() },
|
|
858
875
|
},
|
|
859
876
|
},
|
|
877
|
+
'/v1/commits/{id}': {
|
|
878
|
+
get: {
|
|
879
|
+
tags: ['commits'],
|
|
880
|
+
summary: 'Retrieve a commit record',
|
|
881
|
+
parameters: [idParam()],
|
|
882
|
+
responses: { '200': jsonResp('The commit record, or null when absent', derive(commitRecordSchema.nullable(), 'output')) },
|
|
883
|
+
},
|
|
884
|
+
},
|
|
860
885
|
};
|
|
861
886
|
|
|
862
887
|
applyOperationIds(paths, ABLO_OPERATION_IDS);
|
|
@@ -864,7 +889,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
|
|
|
864
889
|
|
|
865
890
|
return envelope(
|
|
866
891
|
options,
|
|
867
|
-
'
|
|
892
|
+
'Ablo collaboration infrastructure: commit, read, and claim. `{model}` is any model ' +
|
|
868
893
|
'from your pushed schema — the routes are the same whichever it is. ' +
|
|
869
894
|
'Authenticate every request with your API key as a Bearer token.',
|
|
870
895
|
paths,
|
|
@@ -962,6 +987,12 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
962
987
|
}
|
|
963
988
|
|
|
964
989
|
paths['/v1/commits'] = {
|
|
990
|
+
get: {
|
|
991
|
+
tags: ['commits'],
|
|
992
|
+
summary: 'List commit records',
|
|
993
|
+
parameters: queryParams(commitRecordListQuerySchema),
|
|
994
|
+
responses: { '200': jsonResp('Tenant-scoped commit records', derive(commitRecordListSchema, 'output')) },
|
|
995
|
+
},
|
|
965
996
|
post: {
|
|
966
997
|
tags: ['commits'],
|
|
967
998
|
summary: 'Commit a batch of operations atomically, and/or register durable premises',
|
|
@@ -970,7 +1001,17 @@ export function schemaToOpenApi<S extends SchemaRecord>(
|
|
|
970
1001
|
responses: { '200': commitReceipt() },
|
|
971
1002
|
},
|
|
972
1003
|
};
|
|
1004
|
+
paths['/v1/commits/{id}'] = {
|
|
1005
|
+
get: {
|
|
1006
|
+
tags: ['commits'],
|
|
1007
|
+
summary: 'Retrieve a commit record',
|
|
1008
|
+
parameters: [idParam()],
|
|
1009
|
+
responses: { '200': jsonResp('The commit record, or null when absent', derive(commitRecordSchema.nullable(), 'output')) },
|
|
1010
|
+
},
|
|
1011
|
+
};
|
|
1012
|
+
operationIds['GET /v1/commits'] = 'listCommits';
|
|
973
1013
|
operationIds['POST /v1/commits'] = 'commit';
|
|
1014
|
+
operationIds['GET /v1/commits/{id}'] = 'getCommit';
|
|
974
1015
|
applyOperationIds(paths, operationIds);
|
|
975
1016
|
attachCanonicalErrors(paths);
|
|
976
1017
|
|
package/src/server/commit.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type { ParticipantKind, ConfirmationState } from '../log/syncDeltaRow.js'
|
|
|
17
17
|
import type { ParticipantRef } from '../wire/delta.js';
|
|
18
18
|
import type { CommitExecutionResultInput } from '../wire/commit.js';
|
|
19
19
|
import type { ReadDependency, TrackDependency } from '../coordination/schema.js';
|
|
20
|
+
import type { EffectiveAuthority } from '../auth/capability.js';
|
|
20
21
|
|
|
21
22
|
export interface CommitContext {
|
|
22
23
|
participantId: string;
|
|
@@ -25,6 +26,11 @@ export interface CommitContext {
|
|
|
25
26
|
* structured attribution rather than a string-prefix convention.
|
|
26
27
|
*/
|
|
27
28
|
participantKind: ParticipantKind;
|
|
29
|
+
/** Effective data grant resolved from the authenticated identity at commit time. */
|
|
30
|
+
authority?: EffectiveAuthority;
|
|
31
|
+
/** Server request identity and ingress used for physical-attempt evidence. */
|
|
32
|
+
requestId?: string;
|
|
33
|
+
transport?: 'http' | 'websocket' | 'internal';
|
|
28
34
|
organizationId: string;
|
|
29
35
|
/** Immutable branch selected by the authenticated credential. */
|
|
30
36
|
branchId: string;
|
|
@@ -90,7 +96,7 @@ export interface CommitContext {
|
|
|
90
96
|
|
|
91
97
|
/**
|
|
92
98
|
* The server execution receipt persisted in `mutation_log`. Its runtime schema
|
|
93
|
-
* lives with the HTTP/WS
|
|
99
|
+
* lives with the HTTP/WS confirmation contract so queued correlation cannot drift
|
|
94
100
|
* between cache, transport, and client.
|
|
95
101
|
*/
|
|
96
102
|
export type CommitExecutionResult = CommitExecutionResultInput;
|
package/src/server/index.ts
CHANGED
|
@@ -21,6 +21,10 @@ export type {
|
|
|
21
21
|
AdapterResolver,
|
|
22
22
|
} from './adapter.js';
|
|
23
23
|
export type { CommitContext, CommitExecutionResult } from './commit.js';
|
|
24
|
-
export {
|
|
24
|
+
export {
|
|
25
|
+
commitExecutionResultSchema,
|
|
26
|
+
commitRecordSchema,
|
|
27
|
+
type CommitRecord,
|
|
28
|
+
} from '../wire/commit.js';
|
|
25
29
|
export { storageModeSchema, type StorageMode } from './storageMode.js';
|
|
26
30
|
export type { ColumnOverride, BootstrapModel } from './readConfig.js';
|
|
@@ -338,15 +338,23 @@ export function kyselyDirectMutation<S extends SchemaRecord>(
|
|
|
338
338
|
db: KyselyLike,
|
|
339
339
|
schema: Schema<S>,
|
|
340
340
|
): MutationAdapter {
|
|
341
|
+
const markerModels = new Map<string, string>();
|
|
342
|
+
for (const [key, definition] of Object.entries(schema.models)) {
|
|
343
|
+
const typename = definition.typename || key;
|
|
344
|
+
markerModels.set(key, typename);
|
|
345
|
+
markerModels.set(key.toLowerCase(), typename);
|
|
346
|
+
markerModels.set(typename, typename);
|
|
347
|
+
markerModels.set(typename.toLowerCase(), typename);
|
|
348
|
+
}
|
|
341
349
|
return createKyselyMutationAdapter(
|
|
342
350
|
db,
|
|
343
351
|
createKyselyMutationCore(db, schema),
|
|
344
352
|
'direct',
|
|
345
353
|
{
|
|
346
|
-
markerModelFor: (operationModel) =>
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
354
|
+
markerModelFor: (operationModel) =>
|
|
355
|
+
markerModels.get(operationModel) ??
|
|
356
|
+
markerModels.get(operationModel.toLowerCase()) ??
|
|
357
|
+
operationModel,
|
|
350
358
|
},
|
|
351
359
|
);
|
|
352
360
|
}
|
|
@@ -100,11 +100,23 @@ function buildColumnMaps(schema: Schema): ReadonlyMap<string, ModelColumns> {
|
|
|
100
100
|
}
|
|
101
101
|
const orgColumn = tenancyColumn(model.tenancy);
|
|
102
102
|
if (orgColumn) register('organizationId', orgColumn);
|
|
103
|
-
|
|
103
|
+
const columns = {
|
|
104
104
|
table: model.tableName ?? key,
|
|
105
105
|
fieldToColumn,
|
|
106
106
|
columnToField,
|
|
107
|
-
}
|
|
107
|
+
};
|
|
108
|
+
// The public clients normalize operation model names to lowercase before
|
|
109
|
+
// they cross the wire. Preserve the authored key for direct adapter users,
|
|
110
|
+
// but resolve the same lowercase key/typename aliases as the server's
|
|
111
|
+
// schema-derived ModelMap. Without these aliases, a camelCase schema key
|
|
112
|
+
// such as `collaborationWorkItems` validates at the server boundary and
|
|
113
|
+
// then fails here as an unknown customer model.
|
|
114
|
+
out.set(key, columns);
|
|
115
|
+
out.set(key.toLowerCase(), columns);
|
|
116
|
+
if (model.typename) {
|
|
117
|
+
out.set(model.typename, columns);
|
|
118
|
+
out.set(model.typename.toLowerCase(), columns);
|
|
119
|
+
}
|
|
108
120
|
}
|
|
109
121
|
return out;
|
|
110
122
|
}
|
package/src/source/types.ts
CHANGED
|
@@ -152,7 +152,7 @@ export interface SourceEvent {
|
|
|
152
152
|
*/
|
|
153
153
|
readonly organizationId?: string;
|
|
154
154
|
/**
|
|
155
|
-
* @deprecated Legacy echo identity. It is not trusted for queued
|
|
155
|
+
* @deprecated Legacy echo identity. It is not trusted for queued confirmation;
|
|
156
156
|
* use `correlationId` and `transactionId` for mediated endpoint writes.
|
|
157
157
|
*/
|
|
158
158
|
readonly clientTxId?: string;
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
import { listEnvelope } from '@abloatai/transaction/wire';
|
|
19
19
|
import type {
|
|
20
|
+
CommitAck,
|
|
21
|
+
CommitReceiptWire,
|
|
20
22
|
ModelReadResponse,
|
|
21
23
|
ModelListResponse,
|
|
22
24
|
ClaimAcquiredResponse,
|
|
@@ -25,6 +27,55 @@ import type {
|
|
|
25
27
|
ClaimHeartbeatReply,
|
|
26
28
|
} from '@abloatai/transaction/wire';
|
|
27
29
|
import type { ModelClaim } from '@abloatai/transaction/coordination';
|
|
30
|
+
import type { EffectiveAuthority } from '@abloatai/transaction/auth';
|
|
31
|
+
|
|
32
|
+
/** Stable server-authored times shared by commit boundary fixtures. */
|
|
33
|
+
export const COMMIT_FIXTURE_TIMES = {
|
|
34
|
+
createdAt: '2026-08-05T10:00:00.000Z',
|
|
35
|
+
statusAt: '2026-08-05T10:00:00.058Z',
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
/** Canonical authenticated authority for transport-boundary test responses. */
|
|
39
|
+
export const EFFECTIVE_AUTHORITY_FIXTURE = {
|
|
40
|
+
organizationId: 'org-1',
|
|
41
|
+
projectId: 'project-1',
|
|
42
|
+
branchId: 'branch-1',
|
|
43
|
+
syncGroups: ['org:org-1'],
|
|
44
|
+
operations: [],
|
|
45
|
+
participantKind: 'agent',
|
|
46
|
+
participantId: 'agent-1',
|
|
47
|
+
deliveryPartition: null,
|
|
48
|
+
} satisfies EffectiveAuthority;
|
|
49
|
+
|
|
50
|
+
/** A canonical hosted confirmation returned by HTTP or WebSocket. */
|
|
51
|
+
export function confirmedCommitReceiptResponse(args: {
|
|
52
|
+
clientTxId: string;
|
|
53
|
+
lastSyncId?: number;
|
|
54
|
+
serverTxId?: string;
|
|
55
|
+
ops?: number;
|
|
56
|
+
}): CommitReceiptWire {
|
|
57
|
+
const lastSyncId = args.lastSyncId ?? 0;
|
|
58
|
+
return {
|
|
59
|
+
object: 'commit_receipt',
|
|
60
|
+
clientTxId: args.clientTxId,
|
|
61
|
+
serverTxId: args.serverTxId ?? String(lastSyncId),
|
|
62
|
+
...COMMIT_FIXTURE_TIMES,
|
|
63
|
+
success: true,
|
|
64
|
+
authority: EFFECTIVE_AUTHORITY_FIXTURE,
|
|
65
|
+
status: 'confirmed',
|
|
66
|
+
lastSyncId,
|
|
67
|
+
ops: args.ops ?? 1,
|
|
68
|
+
} satisfies CommitReceiptWire;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Canonical normalized acknowledgement for mutation-executor test doubles. */
|
|
72
|
+
export function confirmedCommitAck(lastSyncId = 0): CommitAck {
|
|
73
|
+
return {
|
|
74
|
+
status: 'confirmed',
|
|
75
|
+
statusAt: COMMIT_FIXTURE_TIMES.statusAt,
|
|
76
|
+
lastSyncId,
|
|
77
|
+
} satisfies CommitAck;
|
|
78
|
+
}
|
|
28
79
|
|
|
29
80
|
/** `GET /v1/models/{model}/{id}`. Pass `data: null` for a miss. */
|
|
30
81
|
export function modelReadResponse(args: {
|
|
@@ -51,14 +102,22 @@ export function modelListResponse(args: {
|
|
|
51
102
|
hasMore?: boolean;
|
|
52
103
|
nextCursor?: string | null;
|
|
53
104
|
stamp?: number;
|
|
105
|
+
evidence?: readonly { id: string; stamp: number }[];
|
|
54
106
|
}): ModelListResponse {
|
|
107
|
+
const stamp = args.stamp ?? 0;
|
|
108
|
+
const inferredEvidence = args.data.flatMap((row): { id: string; stamp: number }[] => {
|
|
109
|
+
if (typeof row !== 'object' || row === null) return [];
|
|
110
|
+
const id = (row as { id?: unknown }).id;
|
|
111
|
+
return typeof id === 'string' ? [{ id, stamp }] : [];
|
|
112
|
+
});
|
|
55
113
|
return {
|
|
56
114
|
object: 'list',
|
|
57
115
|
model: args.model,
|
|
58
116
|
data: [...args.data],
|
|
59
117
|
has_more: args.hasMore ?? false,
|
|
60
118
|
next_cursor: args.nextCursor ?? null,
|
|
61
|
-
stamp
|
|
119
|
+
stamp,
|
|
120
|
+
evidence: [...(args.evidence ?? inferredEvidence)],
|
|
62
121
|
} satisfies ModelListResponse;
|
|
63
122
|
}
|
|
64
123
|
|
package/src/transactionLayer.ts
CHANGED
|
@@ -30,7 +30,7 @@ import type { ModelScope } from './types/index.js';
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* A commit's durable acceptance — `{ status: 'queued' }` with the correlation
|
|
33
|
-
* the
|
|
33
|
+
* the confirmation feed later confirms. The wire layer owns the receipt fields;
|
|
34
34
|
* the seam adds the client idempotency identity needed for a safe wait/retry.
|
|
35
35
|
*/
|
|
36
36
|
export type CommitReceipt = ClientCommitReceipt & {
|
|
@@ -8,7 +8,12 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
MAX_READ_SET_ENTRIES,
|
|
13
|
+
readDependencyListSchema,
|
|
14
|
+
readSetProjectionEntryCount,
|
|
15
|
+
trackDependencyListSchema,
|
|
16
|
+
} from '../../coordination/schema.js';
|
|
12
17
|
import { wireCommitOperationSchema } from '../../wire/frames.js';
|
|
13
18
|
import { correlationIdSchema } from '../../wire/commit.js';
|
|
14
19
|
import { idempotencyKeySchema } from './idempotencyKey.js';
|
|
@@ -41,6 +46,7 @@ export const durableCommitOperationSchema = wireCommitOperationSchema
|
|
|
41
46
|
id: true,
|
|
42
47
|
input: true,
|
|
43
48
|
transactionId: true,
|
|
49
|
+
claimId: true,
|
|
44
50
|
readAt: true,
|
|
45
51
|
onStale: true,
|
|
46
52
|
fenceToken: true,
|
|
@@ -50,6 +56,7 @@ export const durableCommitOperationSchema = wireCommitOperationSchema
|
|
|
50
56
|
id: z.string().min(1),
|
|
51
57
|
input: z.record(z.string(), z.unknown()).optional(),
|
|
52
58
|
transactionId: z.string().min(1).optional(),
|
|
59
|
+
claimId: z.string().min(1).optional(),
|
|
53
60
|
options: z
|
|
54
61
|
.strictObject({
|
|
55
62
|
idempotencyKey: z.string().min(1).max(255).nullable().optional(),
|
|
@@ -62,8 +69,11 @@ export type DurableCommitOperation = z.infer<typeof durableCommitOperationSchema
|
|
|
62
69
|
export type DurableCommitOperationInput = z.input<typeof durableCommitOperationSchema>;
|
|
63
70
|
|
|
64
71
|
const durableCommitOptionsSchema = z.strictObject({
|
|
65
|
-
reads:
|
|
66
|
-
track:
|
|
72
|
+
reads: readDependencyListSchema.nullable().optional(),
|
|
73
|
+
track: trackDependencyListSchema.nullable().optional(),
|
|
74
|
+
}).refine((value) => readSetProjectionEntryCount(value) <= MAX_READ_SET_ENTRIES, {
|
|
75
|
+
path: ['reads'],
|
|
76
|
+
message: `reads and track may contain at most ${MAX_READ_SET_ENTRIES} entries combined`,
|
|
67
77
|
});
|
|
68
78
|
|
|
69
79
|
export const commitOutboxScopeSchema = z.strictObject({
|
|
@@ -53,6 +53,7 @@ export interface CommitFrameOperation {
|
|
|
53
53
|
readonly id: string;
|
|
54
54
|
readonly input?: Record<string, unknown>;
|
|
55
55
|
readonly transactionId?: string;
|
|
56
|
+
readonly claimId?: string | null;
|
|
56
57
|
readonly readAt?: number | null;
|
|
57
58
|
readonly onStale?: OnStaleMode | null;
|
|
58
59
|
readonly fenceToken?: number | null;
|
|
@@ -80,17 +81,18 @@ export function buildCommitFrame(
|
|
|
80
81
|
id: op.id,
|
|
81
82
|
input: op.input,
|
|
82
83
|
transactionId: op.transactionId,
|
|
84
|
+
claimId: op.claimId,
|
|
83
85
|
readAt: op.readAt,
|
|
84
86
|
onStale: op.onStale,
|
|
85
87
|
fenceToken: op.fenceToken,
|
|
86
88
|
})),
|
|
87
89
|
clientTxId,
|
|
88
90
|
};
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
if (track
|
|
91
|
+
// Preserve declaration at the wire boundary. `null`, an explicit empty list,
|
|
92
|
+
// and omission are distinct inputs even when today's server treats each as
|
|
93
|
+
// "none"; the WebSocket path must not silently erase a field the caller set.
|
|
94
|
+
if (reads !== undefined) payload.reads = reads === null ? null : [...reads];
|
|
95
|
+
if (track !== undefined) payload.track = track === null ? null : [...track];
|
|
94
96
|
return { type: 'commit', payload };
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* first successful connect and disposed on teardown.
|
|
16
16
|
*
|
|
17
17
|
* This is a plain state machine, not a reactive object: it lives in the
|
|
18
|
-
*
|
|
18
|
+
* confirmation core, which carries no reactivity runtime (ADR 0016). A consumer
|
|
19
19
|
* that wants to render recovery progress mirrors the transitions through
|
|
20
20
|
* {@link ConnectionCallbacks.onStateChange} into its own observable state —
|
|
21
21
|
* which is what the reactive engine's store does.
|