@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.
Files changed (257) hide show
  1. package/CONVENTIONS.md +7 -7
  2. package/README.md +1 -1
  3. package/dist/ai-sdk/modelTools.d.ts +1 -1
  4. package/dist/ai-sdk/modelTools.js +1 -1
  5. package/dist/ai-sdk/updateTool.d.ts +2 -2
  6. package/dist/ai-sdk/updateTool.d.ts.map +1 -1
  7. package/dist/auth/bootstrapScope.d.ts +1 -1
  8. package/dist/auth/capability.d.ts +32 -10
  9. package/dist/auth/capability.d.ts.map +1 -1
  10. package/dist/auth/capability.js +9 -3
  11. package/dist/auth/capability.js.map +1 -1
  12. package/dist/auth/capabilityLifecycle.d.ts +2 -2
  13. package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
  14. package/dist/auth/capabilityLifecycle.js +3 -2
  15. package/dist/auth/capabilityLifecycle.js.map +1 -1
  16. package/dist/auth/identity.d.ts +3 -0
  17. package/dist/auth/identity.d.ts.map +1 -1
  18. package/dist/auth/identity.js +13 -1
  19. package/dist/auth/identity.js.map +1 -1
  20. package/dist/auth/index.d.ts +2 -2
  21. package/dist/auth/index.d.ts.map +1 -1
  22. package/dist/auth/index.js +1 -1
  23. package/dist/auth/index.js.map +1 -1
  24. package/dist/auth/schemas.d.ts +18 -1
  25. package/dist/auth/schemas.d.ts.map +1 -1
  26. package/dist/auth/schemas.js +3 -1
  27. package/dist/auth/schemas.js.map +1 -1
  28. package/dist/auth/sessionMint.d.ts.map +1 -1
  29. package/dist/auth/sessionMint.js +0 -1
  30. package/dist/auth/sessionMint.js.map +1 -1
  31. package/dist/commitRecordRuntime.d.ts +30 -0
  32. package/dist/commitRecordRuntime.d.ts.map +1 -0
  33. package/dist/commitRecordRuntime.js +188 -0
  34. package/dist/commitRecordRuntime.js.map +1 -0
  35. package/dist/coordination/index.d.ts +3 -3
  36. package/dist/coordination/index.d.ts.map +1 -1
  37. package/dist/coordination/index.js +2 -2
  38. package/dist/coordination/index.js.map +1 -1
  39. package/dist/coordination/locator.d.ts +5 -0
  40. package/dist/coordination/locator.d.ts.map +1 -1
  41. package/dist/coordination/locator.js +12 -0
  42. package/dist/coordination/locator.js.map +1 -1
  43. package/dist/coordination/schema.d.ts +205 -15
  44. package/dist/coordination/schema.d.ts.map +1 -1
  45. package/dist/coordination/schema.js +88 -47
  46. package/dist/coordination/schema.js.map +1 -1
  47. package/dist/coordination/targetConflict.js +1 -1
  48. package/dist/durableWrites.d.ts +3 -3
  49. package/dist/durableWrites.d.ts.map +1 -1
  50. package/dist/durableWrites.js +1 -1
  51. package/dist/errorCodes.d.ts +20 -1
  52. package/dist/errorCodes.d.ts.map +1 -1
  53. package/dist/errorCodes.js +44 -10
  54. package/dist/errorCodes.js.map +1 -1
  55. package/dist/errorObservation.d.ts +82 -0
  56. package/dist/errorObservation.d.ts.map +1 -0
  57. package/dist/errorObservation.js +101 -0
  58. package/dist/errorObservation.js.map +1 -0
  59. package/dist/errors.d.ts +2 -0
  60. package/dist/errors.d.ts.map +1 -1
  61. package/dist/errors.js +6 -1
  62. package/dist/errors.js.map +1 -1
  63. package/dist/index.d.ts +5 -2
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +1 -1
  66. package/dist/index.js.map +1 -1
  67. package/dist/internal/read-set.d.ts +8 -0
  68. package/dist/internal/read-set.d.ts.map +1 -0
  69. package/dist/internal/read-set.js +7 -0
  70. package/dist/internal/read-set.js.map +1 -0
  71. package/dist/logger.d.ts +1 -1
  72. package/dist/observability.d.ts +1 -1
  73. package/dist/observability.js +1 -1
  74. package/dist/readSetContext.d.ts +54 -0
  75. package/dist/readSetContext.d.ts.map +1 -0
  76. package/dist/readSetContext.js +98 -0
  77. package/dist/readSetContext.js.map +1 -0
  78. package/dist/resources/functionalUpdate.d.ts +13 -3
  79. package/dist/resources/functionalUpdate.d.ts.map +1 -1
  80. package/dist/resources/functionalUpdate.js.map +1 -1
  81. package/dist/resources/httpResources.d.ts +23 -7
  82. package/dist/resources/httpResources.d.ts.map +1 -1
  83. package/dist/resources/modelOperations.d.ts +2 -2
  84. package/dist/resources/modelOperations.js +1 -1
  85. package/dist/resources/mutationOptions.d.ts +1 -1
  86. package/dist/resources/mutationOptions.js +1 -1
  87. package/dist/resources/where.d.ts +1 -1
  88. package/dist/resources/where.js +1 -1
  89. package/dist/resources/writeOptionsSchema.d.ts +39 -4
  90. package/dist/resources/writeOptionsSchema.d.ts.map +1 -1
  91. package/dist/resources/writeOptionsSchema.js +12 -6
  92. package/dist/resources/writeOptionsSchema.js.map +1 -1
  93. package/dist/schema/index.js +1 -1
  94. package/dist/schema/index.js.map +1 -1
  95. package/dist/schema/openapi.d.ts.map +1 -1
  96. package/dist/schema/openapi.js +41 -7
  97. package/dist/schema/openapi.js.map +1 -1
  98. package/dist/server/commit.d.ts +7 -1
  99. package/dist/server/commit.d.ts.map +1 -1
  100. package/dist/server/index.d.ts +1 -1
  101. package/dist/server/index.d.ts.map +1 -1
  102. package/dist/server/index.js +1 -1
  103. package/dist/server/index.js.map +1 -1
  104. package/dist/source/adapters/kysely.d.ts.map +1 -1
  105. package/dist/source/adapters/kysely.js +11 -4
  106. package/dist/source/adapters/kysely.js.map +1 -1
  107. package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -1
  108. package/dist/source/adapters/kyselyMutationCore.js +14 -2
  109. package/dist/source/adapters/kyselyMutationCore.js.map +1 -1
  110. package/dist/source/contract.d.ts +2 -0
  111. package/dist/source/contract.d.ts.map +1 -1
  112. package/dist/source/types.d.ts +1 -1
  113. package/dist/testing/fixtures/httpResponses.d.ts +30 -1
  114. package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
  115. package/dist/testing/fixtures/httpResponses.js +48 -1
  116. package/dist/testing/fixtures/httpResponses.js.map +1 -1
  117. package/dist/transactionLayer.d.ts +1 -1
  118. package/dist/transactions/{settlement → confirmation}/commitEnvelope.d.ts +4 -2
  119. package/dist/transactions/confirmation/commitEnvelope.d.ts.map +1 -0
  120. package/dist/transactions/{settlement → confirmation}/commitEnvelope.js +8 -3
  121. package/dist/transactions/confirmation/commitEnvelope.js.map +1 -0
  122. package/dist/transactions/confirmation/httpCommitEnvelope.d.ts.map +1 -0
  123. package/dist/transactions/confirmation/httpCommitEnvelope.js.map +1 -0
  124. package/dist/transactions/confirmation/idempotencyKey.d.ts.map +1 -0
  125. package/dist/transactions/confirmation/idempotencyKey.js.map +1 -0
  126. package/dist/transactions/{settlement → confirmation}/pendingWrite.d.ts +3 -2
  127. package/dist/transactions/confirmation/pendingWrite.d.ts.map +1 -0
  128. package/dist/transactions/confirmation/pendingWrite.js.map +1 -0
  129. package/dist/transport/commitFrames.d.ts +1 -0
  130. package/dist/transport/commitFrames.d.ts.map +1 -1
  131. package/dist/transport/commitFrames.js +8 -7
  132. package/dist/transport/commitFrames.js.map +1 -1
  133. package/dist/transport/connectionManager.d.ts +1 -1
  134. package/dist/transport/connectionManager.js +1 -1
  135. package/dist/transport/httpClient.d.ts +23 -9
  136. package/dist/transport/httpClient.d.ts.map +1 -1
  137. package/dist/transport/httpClient.js +134 -15
  138. package/dist/transport/httpClient.js.map +1 -1
  139. package/dist/transport/httpOptions.d.ts +9 -1
  140. package/dist/transport/httpOptions.d.ts.map +1 -1
  141. package/dist/transport/httpTransport.d.ts +3 -0
  142. package/dist/transport/httpTransport.d.ts.map +1 -1
  143. package/dist/transport/httpTransport.js +109 -22
  144. package/dist/transport/httpTransport.js.map +1 -1
  145. package/dist/transport/wsFrameHandlers.d.ts +3 -1
  146. package/dist/transport/wsFrameHandlers.d.ts.map +1 -1
  147. package/dist/transport/wsFrameHandlers.js +38 -17
  148. package/dist/transport/wsFrameHandlers.js.map +1 -1
  149. package/dist/transport/wsTransport.d.ts +3 -0
  150. package/dist/transport/wsTransport.d.ts.map +1 -1
  151. package/dist/transport/wsTransport.js +32 -1
  152. package/dist/transport/wsTransport.js.map +1 -1
  153. package/dist/utils/json.d.ts +1 -1
  154. package/dist/utils/json.js +1 -1
  155. package/dist/wire/accountResponses.d.ts +1 -1
  156. package/dist/wire/auth.d.ts +2 -2
  157. package/dist/wire/commit.d.ts +1652 -213
  158. package/dist/wire/commit.d.ts.map +1 -1
  159. package/dist/wire/commit.js +196 -132
  160. package/dist/wire/commit.js.map +1 -1
  161. package/dist/wire/delta.d.ts +1 -1
  162. package/dist/wire/errorEnvelope.d.ts +1 -0
  163. package/dist/wire/errorEnvelope.d.ts.map +1 -1
  164. package/dist/wire/errorEnvelope.js +1 -0
  165. package/dist/wire/errorEnvelope.js.map +1 -1
  166. package/dist/wire/feedCursor.d.ts +1 -1
  167. package/dist/wire/feedCursor.js +1 -1
  168. package/dist/wire/feedEvent.d.ts +3 -3
  169. package/dist/wire/frames.d.ts +8 -4
  170. package/dist/wire/frames.d.ts.map +1 -1
  171. package/dist/wire/frames.js +6 -3
  172. package/dist/wire/frames.js.map +1 -1
  173. package/dist/wire/inboundFrames.d.ts +44 -4
  174. package/dist/wire/inboundFrames.d.ts.map +1 -1
  175. package/dist/wire/index.d.ts +6 -4
  176. package/dist/wire/index.d.ts.map +1 -1
  177. package/dist/wire/index.js +6 -5
  178. package/dist/wire/index.js.map +1 -1
  179. package/dist/wire/modelMutations.d.ts +35 -0
  180. package/dist/wire/modelMutations.d.ts.map +1 -1
  181. package/dist/wire/modelMutations.js +9 -2
  182. package/dist/wire/modelMutations.js.map +1 -1
  183. package/dist/wire/modelResponses.d.ts +12 -1
  184. package/dist/wire/modelResponses.d.ts.map +1 -1
  185. package/dist/wire/modelResponses.js +12 -1
  186. package/dist/wire/modelResponses.js.map +1 -1
  187. package/package.json +12 -2
  188. package/src/ai-sdk/modelTools.ts +1 -1
  189. package/src/ai-sdk/updateTool.ts +2 -2
  190. package/src/auth/bootstrapScope.ts +1 -1
  191. package/src/auth/capability.ts +13 -4
  192. package/src/auth/capabilityLifecycle.ts +3 -2
  193. package/src/auth/identity.ts +16 -1
  194. package/src/auth/index.ts +2 -0
  195. package/src/auth/schemas.ts +3 -0
  196. package/src/auth/sessionMint.ts +0 -1
  197. package/src/commitRecordRuntime.ts +245 -0
  198. package/src/coordination/index.ts +24 -1
  199. package/src/coordination/locator.ts +17 -0
  200. package/src/coordination/schema.ts +114 -49
  201. package/src/coordination/targetConflict.ts +1 -1
  202. package/src/durableWrites.ts +3 -3
  203. package/src/errorCodes.ts +107 -11
  204. package/src/errorObservation.ts +121 -0
  205. package/src/errors.ts +5 -1
  206. package/src/index.ts +8 -1
  207. package/src/internal/read-set.ts +18 -0
  208. package/src/logger.ts +1 -1
  209. package/src/observability.ts +1 -1
  210. package/src/readSetContext.ts +164 -0
  211. package/src/resources/functionalUpdate.ts +16 -4
  212. package/src/resources/httpResources.ts +27 -8
  213. package/src/resources/modelOperations.ts +2 -2
  214. package/src/resources/mutationOptions.ts +1 -1
  215. package/src/resources/where.ts +1 -1
  216. package/src/resources/writeOptionsSchema.ts +30 -12
  217. package/src/schema/index.ts +1 -1
  218. package/src/schema/openapi.ts +48 -7
  219. package/src/server/commit.ts +7 -1
  220. package/src/server/index.ts +5 -1
  221. package/src/source/adapters/kysely.ts +12 -4
  222. package/src/source/adapters/kyselyMutationCore.ts +14 -2
  223. package/src/source/types.ts +1 -1
  224. package/src/testing/fixtures/httpResponses.ts +60 -1
  225. package/src/transactionLayer.ts +1 -1
  226. package/src/transactions/{settlement → confirmation}/commitEnvelope.ts +13 -3
  227. package/src/transport/commitFrames.ts +7 -5
  228. package/src/transport/connectionManager.ts +1 -1
  229. package/src/transport/httpClient.ts +256 -29
  230. package/src/transport/httpOptions.ts +9 -1
  231. package/src/transport/httpTransport.ts +139 -27
  232. package/src/transport/wsFrameHandlers.ts +46 -8
  233. package/src/transport/wsTransport.ts +42 -1
  234. package/src/utils/json.ts +1 -1
  235. package/src/wire/commit.ts +248 -147
  236. package/src/wire/errorEnvelope.ts +1 -0
  237. package/src/wire/feedCursor.ts +1 -1
  238. package/src/wire/frames.ts +9 -4
  239. package/src/wire/index.ts +38 -5
  240. package/src/wire/modelMutations.ts +16 -2
  241. package/src/wire/modelResponses.ts +14 -1
  242. package/dist/transactions/settlement/commitEnvelope.d.ts.map +0 -1
  243. package/dist/transactions/settlement/commitEnvelope.js.map +0 -1
  244. package/dist/transactions/settlement/httpCommitEnvelope.d.ts.map +0 -1
  245. package/dist/transactions/settlement/httpCommitEnvelope.js.map +0 -1
  246. package/dist/transactions/settlement/idempotencyKey.d.ts.map +0 -1
  247. package/dist/transactions/settlement/idempotencyKey.js.map +0 -1
  248. package/dist/transactions/settlement/pendingWrite.d.ts.map +0 -1
  249. package/dist/transactions/settlement/pendingWrite.js.map +0 -1
  250. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.d.ts +0 -0
  251. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.js +0 -0
  252. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.d.ts +0 -0
  253. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.js +0 -0
  254. /package/dist/transactions/{settlement → confirmation}/pendingWrite.js +0 -0
  255. /package/src/transactions/{settlement → confirmation}/httpCommitEnvelope.ts +0 -0
  256. /package/src/transactions/{settlement → confirmation}/idempotencyKey.ts +0 -0
  257. /package/src/transactions/{settlement → confirmation}/pendingWrite.ts +0 -0
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Canonical runtime contracts for commit settlement.
2
+ * Canonical runtime contracts for commit status.
3
3
  *
4
4
  * A commit crosses several boundaries during its lifetime: the server's
5
5
  * execution cache, the HTTP/WS receipt, and the client's acknowledgement
6
6
  * tracker. Those boundaries intentionally have different envelopes, but they
7
- * all compose the same settlement vocabulary from this module:
7
+ * all compose the same lifecycle vocabulary from this module:
8
8
  *
9
- * - `confirmed` — the authoritative change is visible at a sync watermark.
9
+ * - `confirmed` — the authoritative change is visible through `lastSyncId`.
10
10
  * - `queued` — a connected source durably accepted the write and only its
11
11
  * correlated authoritative source delta (WAL for direct, endpoint event
12
12
  * for endpoint-only), identified by `correlationId`, may promote it.
@@ -20,12 +20,19 @@
20
20
  import { z } from 'zod';
21
21
  import {
22
22
  onStaleModeSchema,
23
- readDependencySchema,
23
+ MAX_READ_SET_ENTRIES,
24
+ readDependencyListSchema,
25
+ readSetRowTargetSchema,
26
+ readSetProjectionEntryCount,
27
+ readSetSchema,
28
+ readSetWatermarkSchema,
29
+ participantKindSchema,
24
30
  staleNotificationSchema,
25
- trackDependencySchema,
31
+ trackDependencyListSchema,
26
32
  } from '../coordination/schema.js';
27
33
  import type { ErrorCode } from '../errorCodes.js';
28
34
  import { requiredCapabilityWireSchema } from '../errors.js';
35
+ import { effectiveAuthoritySchema } from '../auth/capability.js';
29
36
 
30
37
  /** Matches the permanent source/idempotency-key ceiling. */
31
38
  export const COMMIT_CORRELATION_ID_MAX_LENGTH = 255;
@@ -34,47 +41,107 @@ export const COMMIT_CORRELATION_ID_MAX_LENGTH = 255;
34
41
  export const correlationIdSchema = z.string().min(1).max(COMMIT_CORRELATION_ID_MAX_LENGTH);
35
42
  export type CorrelationId = z.infer<typeof correlationIdSchema>;
36
43
 
37
- export const commitStatusSchema = z.enum(['queued', 'confirmed']);
38
- export type CommitStatus = z.infer<typeof commitStatusSchema>;
44
+ /** Public, server-authored commit timestamps. */
45
+ export const commitTimestampSchema = z.iso.datetime({ offset: true });
46
+
47
+ // These are the only lifecycle-literal declarations in transaction source.
48
+ // Every boundary below composes these schemas instead of spelling a status.
49
+ export const queuedStatusSchema = z.literal('queued');
50
+ export const confirmedStatusSchema = z.literal('confirmed');
51
+ export const rejectedStatusSchema = z.literal('rejected');
52
+
53
+ export const queuedCommitStatusSchema = z.strictObject({
54
+ status: queuedStatusSchema,
55
+ statusAt: commitTimestampSchema,
56
+ lastSyncId: z.literal(0),
57
+ correlationId: correlationIdSchema,
58
+ });
59
+
60
+ export const confirmedCommitStatusSchema = z
61
+ .strictObject({
62
+ status: confirmedStatusSchema,
63
+ statusAt: commitTimestampSchema,
64
+ lastSyncId: readSetWatermarkSchema,
65
+ correlationId: correlationIdSchema.optional(),
66
+ })
67
+ .refine(({ correlationId, lastSyncId }) => correlationId === undefined || lastSyncId > 0, {
68
+ path: ['lastSyncId'],
69
+ message: 'A source-confirmed commit requires a positive lastSyncId',
70
+ });
71
+
72
+ export const rejectedCommitStatusSchema = z.strictObject({
73
+ status: rejectedStatusSchema,
74
+ statusAt: commitTimestampSchema,
75
+ });
76
+
77
+ /** The single semantic owner of the complete commit lifecycle fact. */
78
+ export const commitStatusSchema = z.discriminatedUnion('status', [
79
+ queuedCommitStatusSchema,
80
+ confirmedCommitStatusSchema,
81
+ rejectedCommitStatusSchema,
82
+ ]);
83
+ export type CommitStatusValue = z.infer<typeof commitStatusSchema>;
84
+ export type CommitStatus = CommitStatusValue['status'];
39
85
 
40
86
  /**
41
- * The settlement states a write may block on — extracted from the reported
87
+ * The lifecycle states a write may block on — extracted from the reported
42
88
  * vocabulary above, never restated beside it. `commitStatusSchema` is every
43
89
  * state a server can report; this is the subset a caller can usefully wait for,
44
90
  * and the two are not the same question: one describes an outcome, the other a
45
91
  * request.
46
92
  *
47
93
  * `.extract` keeps the relationship mechanical in both directions. Renaming a
48
- * settlement state breaks this line at build time, and adding one that nobody
94
+ * lifecycle state breaks this line at build time, and adding one that nobody
49
95
  * can block on — a commit parked for a human, say — does not become a legal
50
96
  * `wait` merely by being added above. Both the `wait` option's runtime
51
97
  * validator and its interface derive from here, so they cannot disagree.
52
98
  */
53
- export const commitWaitSchema = commitStatusSchema.extract(['queued', 'confirmed']);
99
+ export const commitWaitSchema = z.union([queuedStatusSchema, confirmedStatusSchema]);
54
100
  export type CommitWait = z.infer<typeof commitWaitSchema>;
55
101
 
56
- const queuedSettlementShape = {
57
- status: z.literal('queued'),
58
- correlationId: correlationIdSchema,
59
- } as const;
102
+ const missingIdsSchema = z.array(z.string().min(1));
103
+ const notificationsSchema = z.array(staleNotificationSchema);
60
104
 
61
- const confirmedSettlementShape = {
62
- status: z.literal('confirmed'),
63
- correlationId: correlationIdSchema.optional(),
64
- } as const;
105
+ export const commitActorSchema = z.strictObject({
106
+ kind: participantKindSchema,
107
+ id: z.string().min(1),
108
+ });
109
+ export type CommitActor = z.infer<typeof commitActorSchema>;
65
110
 
66
- const queuedSettlementSchema = z.strictObject(queuedSettlementShape);
67
- const confirmedSettlementSchema = z.strictObject(confirmedSettlementShape);
111
+ export const commitAttemptSchema = z.strictObject({
112
+ id: z.string().min(1),
113
+ observedAt: commitTimestampSchema,
114
+ transport: z.enum(['http', 'websocket', 'internal']),
115
+ kind: z.enum(['execution', 'replay']),
116
+ });
117
+ export type CommitAttempt = z.infer<typeof commitAttemptSchema>;
68
118
 
69
- /** The one settlement discriminant shared by every commit boundary. */
70
- export const commitSettlementSchema = z.discriminatedUnion('status', [
71
- queuedSettlementSchema,
72
- confirmedSettlementSchema,
73
- ]);
74
- export type CommitSettlement = z.infer<typeof commitSettlementSchema>;
119
+ export const commitClaimReferenceSchema = z.strictObject({
120
+ id: z.string().min(1),
121
+ target: readSetRowTargetSchema,
122
+ fenceToken: z.number().int().nonnegative(),
123
+ });
124
+ export type CommitClaimReference = z.infer<typeof commitClaimReferenceSchema>;
75
125
 
76
- const missingIdsSchema = z.array(z.string().min(1));
77
- const notificationsSchema = z.array(staleNotificationSchema);
126
+ const commitEvidenceShape = {
127
+ attempts: z.array(commitAttemptSchema).min(1).readonly(),
128
+ actor: commitActorSchema,
129
+ authority: effectiveAuthoritySchema,
130
+ claims: z.array(commitClaimReferenceSchema).readonly(),
131
+ } as const;
132
+
133
+ function requireNonnegativeStatusLatency(
134
+ value: { readonly createdAt: string; readonly statusAt: string },
135
+ context: z.RefinementCtx,
136
+ ): void {
137
+ if (Date.parse(value.statusAt) < Date.parse(value.createdAt)) {
138
+ context.addIssue({
139
+ code: 'custom',
140
+ path: ['statusAt'],
141
+ message: 'Commit statusAt cannot precede createdAt',
142
+ });
143
+ }
144
+ }
78
145
 
79
146
  const successfulReceiptCommonShape = {
80
147
  object: z.literal('commit_receipt'),
@@ -82,53 +149,31 @@ const successfulReceiptCommonShape = {
82
149
  id: z.string().min(1).optional(),
83
150
  clientTxId: z.string().min(1),
84
151
  serverTxId: z.string().min(1),
152
+ createdAt: commitTimestampSchema,
85
153
  success: z.literal(true),
86
- lastSyncId: z.number().int().nonnegative(),
87
154
  ops: z.number().int().nonnegative(),
155
+ authority: effectiveAuthoritySchema,
88
156
  notifications: notificationsSchema.optional(),
89
157
  missingIds: missingIdsSchema.optional(),
90
158
  } as const;
91
159
 
92
- const queuedCommitReceiptSchema = z
93
- .object({
94
- ...successfulReceiptCommonShape,
95
- ...queuedSettlementShape,
96
- })
97
- .superRefine((receipt, context) => {
98
- if (receipt.lastSyncId !== 0) {
99
- context.addIssue({
100
- code: 'custom',
101
- path: ['lastSyncId'],
102
- message: 'A queued source receipt cannot claim a confirmed sync watermark',
103
- });
104
- }
105
- });
160
+ const queuedCommitReceiptSchema = queuedCommitStatusSchema.safeExtend(
161
+ successfulReceiptCommonShape,
162
+ );
106
163
 
107
- const confirmedCommitReceiptSchema = z
108
- .object({
109
- ...successfulReceiptCommonShape,
110
- ...confirmedSettlementShape,
111
- })
112
- .superRefine((receipt, context) => {
113
- if (receipt.correlationId && receipt.lastSyncId <= 0) {
114
- context.addIssue({
115
- code: 'custom',
116
- path: ['lastSyncId'],
117
- message: 'A source-confirmed receipt requires a positive sync watermark',
118
- });
119
- }
120
- });
164
+ const confirmedCommitReceiptSchema = confirmedCommitStatusSchema.safeExtend(
165
+ successfulReceiptCommonShape,
166
+ );
121
167
 
122
168
  /**
123
169
  * Successful HTTP/WS receipt. This schema is deliberately strict about the
124
- * settlement discriminant. Zod's default object behavior strips additive
170
+ * lifecycle discriminant. Zod's default object behavior strips additive
125
171
  * fields, which is forward compatible without allowing server-internal
126
172
  * recovery evidence to leak through a parsed wire receipt.
127
173
  */
128
- export const commitReceiptSchema = z.discriminatedUnion('status', [
129
- queuedCommitReceiptSchema,
130
- confirmedCommitReceiptSchema,
131
- ]);
174
+ export const commitReceiptSchema = z
175
+ .discriminatedUnion('status', [queuedCommitReceiptSchema, confirmedCommitReceiptSchema])
176
+ .superRefine(requireNonnegativeStatusLatency);
132
177
  export type CommitReceiptWire = z.infer<typeof commitReceiptSchema>;
133
178
 
134
179
  /**
@@ -139,29 +184,31 @@ export type CommitReceiptWire = z.infer<typeof commitReceiptSchema>;
139
184
  * single boundary cast; every code *producer* is constrained at the
140
185
  * `AbloError` constructor instead.
141
186
  */
142
- const errorCodeSchema = z.custom<ErrorCode>(
143
- (value) => typeof value === 'string' && value.length > 0,
144
- { message: 'commit rejection code must be a non-empty string' }
145
- );
187
+ const errorCodeSchema = z.string().min(1) as z.ZodType<ErrorCode>;
188
+ const commitErrorSchema = z.object({
189
+ code: errorCodeSchema,
190
+ message: z.string(),
191
+ field: z.string().optional(),
192
+ request_id: z.string().optional(),
193
+ event_id: z.string().optional(),
194
+ requiredCapability: requiredCapabilityWireSchema.optional(),
195
+ details: z.record(z.string(), z.unknown()).optional(),
196
+ });
146
197
 
147
198
  /** The failure arm is separate: queued/confirmed always imply `success:true`. */
148
- export const rejectedCommitReceiptSchema = z.object({
149
- object: z.literal('commit_receipt'),
150
- clientTxId: z.string(),
151
- serverTxId: z.string(),
152
- success: z.literal(false),
153
- status: z.literal('rejected'),
154
- lastSyncId: z.number().int().nonnegative().optional(),
155
- ops: z.number().int().nonnegative().optional(),
156
- error: z.object({
157
- code: errorCodeSchema,
158
- message: z.string(),
159
- field: z.string().optional(),
160
- request_id: z.string().optional(),
161
- requiredCapability: requiredCapabilityWireSchema.optional(),
162
- details: z.record(z.string(), z.unknown()).optional(),
163
- }),
164
- });
199
+ export const rejectedCommitReceiptSchema = rejectedCommitStatusSchema
200
+ .safeExtend({
201
+ object: z.literal('commit_receipt'),
202
+ clientTxId: z.string(),
203
+ serverTxId: z.string(),
204
+ createdAt: commitTimestampSchema,
205
+ success: z.literal(false),
206
+ ops: z.number().int().nonnegative().optional(),
207
+ /** Server-stamped authority; request bodies have no authority field. */
208
+ authority: effectiveAuthoritySchema,
209
+ error: commitErrorSchema,
210
+ })
211
+ .superRefine(requireNonnegativeStatusLatency);
165
212
  export type RejectedCommitReceiptWire = z.infer<typeof rejectedCommitReceiptSchema>;
166
213
 
167
214
  export const mutationResultPayloadSchema = z.union([
@@ -184,24 +231,32 @@ const confirmationTransactionIdsSchema = z
184
231
  });
185
232
 
186
233
  /**
187
- * Server-internal execution result persisted in `mutation_log`.
188
- *
189
- * `status` remains optional only for pre-settlement hosted rows. At runtime an
190
- * omitted value has the established meaning `confirmed`; new source-forwarded
191
- * rows must use the explicit queued arm with both public correlation and exact
192
- * internal storage ids.
234
+ * Server-internal execution result persisted in `mutation_log`. It composes
235
+ * the same required status fact as the public boundaries plus internal sync
236
+ * range and recovery evidence.
193
237
  */
194
- const rawCommitExecutionResultSchema = z
195
- .strictObject({
196
- lastSyncId: z.number().int().nonnegative(),
238
+ const executionEvidenceShape = {
197
239
  firstSyncId: z.number().int().nonnegative(),
198
- status: commitStatusSchema.optional(),
199
- correlationId: correlationIdSchema.optional(),
240
+ createdAt: commitTimestampSchema,
200
241
  confirmationTransactionIds: confirmationTransactionIdsSchema.optional(),
201
242
  notifications: notificationsSchema.optional(),
202
243
  missingIds: missingIdsSchema.optional(),
203
- })
244
+ } as const;
245
+
246
+ const queuedCommitExecutionResultSchema = queuedCommitStatusSchema.safeExtend(
247
+ executionEvidenceShape,
248
+ );
249
+ const confirmedCommitExecutionResultSchema = confirmedCommitStatusSchema.safeExtend(
250
+ executionEvidenceShape,
251
+ );
252
+
253
+ const canonicalCommitExecutionResultSchema = z
254
+ .discriminatedUnion('status', [
255
+ queuedCommitExecutionResultSchema,
256
+ confirmedCommitExecutionResultSchema,
257
+ ])
204
258
  .superRefine((result, context) => {
259
+ requireNonnegativeStatusLatency(result, context);
205
260
  const validRange =
206
261
  (result.firstSyncId === 0 && result.lastSyncId === 0) ||
207
262
  (result.firstSyncId > 0 && result.firstSyncId <= result.lastSyncId);
@@ -213,14 +268,7 @@ const rawCommitExecutionResultSchema = z
213
268
  });
214
269
  }
215
270
 
216
- if (result.status === 'queued') {
217
- if (!result.correlationId) {
218
- context.addIssue({
219
- code: 'custom',
220
- path: ['correlationId'],
221
- message: 'A queued source result requires a correlationId',
222
- });
223
- }
271
+ if (result.status === queuedStatusSchema.value) {
224
272
  if (!result.confirmationTransactionIds) {
225
273
  context.addIssue({
226
274
  code: 'custom',
@@ -251,18 +299,7 @@ const rawCommitExecutionResultSchema = z
251
299
  message: 'A source correlation requires exact confirmation transaction ids',
252
300
  });
253
301
  }
254
- if (result.correlationId && result.status === undefined) {
255
- context.addIssue({
256
- code: 'custom',
257
- path: ['status'],
258
- message: 'Source-correlated results require explicit settlement status',
259
- });
260
- }
261
- if (
262
- result.status === 'confirmed' &&
263
- result.correlationId &&
264
- (result.firstSyncId <= 0 || result.lastSyncId <= 0)
265
- ) {
302
+ if (result.status === confirmedStatusSchema.value && result.correlationId && result.firstSyncId <= 0) {
266
303
  context.addIssue({
267
304
  code: 'custom',
268
305
  path: ['lastSyncId'],
@@ -270,45 +307,27 @@ const rawCommitExecutionResultSchema = z
270
307
  });
271
308
  }
272
309
  });
273
- export const commitExecutionResultSchema = rawCommitExecutionResultSchema.transform((result) => ({
274
- ...result,
275
- status: result.status ?? ('confirmed' as const),
276
- }));
310
+
311
+ /** Canonical storage contract. Status is never optional or reconstructed. */
312
+ export const commitExecutionResultSchema = canonicalCommitExecutionResultSchema;
277
313
  export type CommitExecutionResultInput = z.input<typeof commitExecutionResultSchema>;
278
314
  export type CommitExecutionResult = z.infer<typeof commitExecutionResultSchema>;
279
315
 
280
316
  const ackCommonShape = {
281
- lastSyncId: z.number().int().nonnegative(),
282
317
  notifications: notificationsSchema.optional(),
283
318
  missingIds: missingIdsSchema.optional(),
284
319
  } as const;
285
320
 
286
321
  /** Normalized acknowledgement handed from a mutation transport to the queue. */
287
322
  export const commitAckSchema = z.discriminatedUnion('status', [
288
- z
289
- .strictObject({
290
- ...ackCommonShape,
291
- ...queuedSettlementShape,
292
- })
293
- .refine(({ lastSyncId }) => lastSyncId === 0, {
294
- path: ['lastSyncId'],
295
- message: 'A queued acknowledgement cannot claim a sync watermark',
296
- }),
297
- z
298
- .strictObject({
299
- ...ackCommonShape,
300
- ...confirmedSettlementShape,
301
- })
302
- .refine(({ correlationId, lastSyncId }) => correlationId === undefined || lastSyncId > 0, {
303
- path: ['lastSyncId'],
304
- message: 'A source-confirmed acknowledgement requires a sync watermark',
305
- }),
323
+ queuedCommitStatusSchema.safeExtend(ackCommonShape),
324
+ confirmedCommitStatusSchema.safeExtend(ackCommonShape),
306
325
  ]);
307
326
  export type CommitAck = z.infer<typeof commitAckSchema>;
308
327
 
309
328
  /**
310
329
  * The boundary an injected mutation executor returns through. It is the
311
- * acknowledgement schema itself: settlement is declared, never inferred from
330
+ * acknowledgement schema itself: status is declared, never inferred from
312
331
  * an omission, so a transport cannot report a write as landed by staying
313
332
  * silent about how it landed.
314
333
  */
@@ -319,16 +338,16 @@ export type MutationCommitResult = z.infer<typeof mutationCommitResultSchema>;
319
338
  /**
320
339
  * Public SDK projection returned by `ablo.commits` and model mutations.
321
340
  *
322
- * This intentionally does not compose `commitSettlementSchema`: on the WS
341
+ * This intentionally carries only the status name: on the WS
323
342
  * facade, `wait:'queued'` currently means locally sealed/enqueued, before a
324
343
  * server has necessarily accepted the write. Renaming that public state to
325
- * `enqueued` is a separate protocol change; the authoritative settlement
344
+ * `enqueued` is a separate protocol change; the authoritative status
326
345
  * union above is reserved for server acknowledgements.
327
346
  */
328
347
  export const clientCommitReceiptSchema = z.strictObject({
329
348
  id: z.string().min(1),
330
- status: commitStatusSchema,
331
- lastSyncId: z.number().int().nonnegative().optional(),
349
+ status: z.union([queuedStatusSchema, confirmedStatusSchema]),
350
+ lastSyncId: readSetWatermarkSchema.optional(),
332
351
  notifications: notificationsSchema.optional(),
333
352
  missingIds: missingIdsSchema.optional(),
334
353
  });
@@ -351,7 +370,8 @@ export type ClientCommitReceipt = z.infer<typeof clientCommitReceiptSchema>;
351
370
  export const commitOperationControlShape = {
352
371
  id: z.string().nullish(),
353
372
  transactionId: z.string().nullish(),
354
- readAt: z.number().nullish(),
373
+ claimId: z.string().min(1).nullish(),
374
+ readAt: readSetWatermarkSchema.nullish(),
355
375
  onStale: onStaleModeSchema.nullish(),
356
376
  fenceToken: z.number().nullish(),
357
377
  };
@@ -365,6 +385,24 @@ export const commitOperationBodySchema = z.object({
365
385
  });
366
386
  export type CommitOperationBody = z.infer<typeof commitOperationBodySchema>;
367
387
 
388
+ /** Commit records retain intent metadata but never copy customer mutation data. */
389
+ export const COMMIT_OPERATION_DATA_RETENTION = 'redacted' as const;
390
+ export const commitRecordOperationSchema = commitOperationBodySchema
391
+ .omit({ data: true })
392
+ .safeExtend({ data: z.strictObject({ retention: z.literal(COMMIT_OPERATION_DATA_RETENTION) }) });
393
+ export type CommitRecordOperation = z.infer<typeof commitRecordOperationSchema>;
394
+
395
+ /** Transport/result evidence nested beneath the canonical record status. */
396
+ export const commitReceiptEvidenceSchema = z.strictObject({
397
+ clientTxId: z.string().min(1),
398
+ serverTxId: z.string(),
399
+ ops: z.number().int().nonnegative().optional(),
400
+ notifications: notificationsSchema.optional(),
401
+ missingIds: missingIdsSchema.optional(),
402
+ error: commitErrorSchema.optional(),
403
+ });
404
+ export type CommitReceiptEvidence = z.infer<typeof commitReceiptEvidenceSchema>;
405
+
368
406
  /**
369
407
  * The `POST /v1/commits` request body.
370
408
  *
@@ -373,9 +411,72 @@ export type CommitOperationBody = z.infer<typeof commitOperationBodySchema>;
373
411
  * `operations` is how a caller starts watching a row without writing to it.
374
412
  * Request identity travels in the `Idempotency-Key` header.
375
413
  */
376
- export const commitRequestSchema = z.object({
414
+ export const commitRequestSchema = z.strictObject({
377
415
  operations: z.array(commitOperationBodySchema).min(1).optional(),
378
- reads: z.array(readDependencySchema).nullish(),
379
- track: z.array(trackDependencySchema).nullish(),
416
+ reads: readDependencyListSchema.nullish(),
417
+ track: trackDependencyListSchema.nullish(),
418
+ }).refine((value) => readSetProjectionEntryCount(value) <= MAX_READ_SET_ENTRIES, {
419
+ path: ['reads'],
420
+ message: `reads and track may contain at most ${MAX_READ_SET_ENTRIES} entries combined`,
380
421
  });
381
422
  export type CommitRequest = z.infer<typeof commitRequestSchema>;
423
+
424
+ /**
425
+ * Current canonical commit projection. Status, time, lastSyncId, and source
426
+ * correlation occur once at the top level; receipt retains transport evidence.
427
+ */
428
+ const commitRecordEvidenceShape = {
429
+ id: z.string().min(1),
430
+ ...commitEvidenceShape,
431
+ createdAt: commitTimestampSchema,
432
+ readSet: readSetSchema,
433
+ operations: z.array(commitRecordOperationSchema).readonly(),
434
+ receipt: commitReceiptEvidenceSchema,
435
+ } as const;
436
+
437
+ export const commitRecordSchema = z
438
+ .discriminatedUnion('status', [
439
+ queuedCommitStatusSchema.safeExtend(commitRecordEvidenceShape),
440
+ confirmedCommitStatusSchema.safeExtend(commitRecordEvidenceShape),
441
+ rejectedCommitStatusSchema.safeExtend({
442
+ ...commitRecordEvidenceShape,
443
+ lastSyncId: z.literal(0),
444
+ }),
445
+ ])
446
+ .superRefine((record, context) => {
447
+ requireNonnegativeStatusLatency(record, context);
448
+ if (record.status === rejectedStatusSchema.value && !record.receipt.error) {
449
+ context.addIssue({
450
+ code: 'custom',
451
+ path: ['receipt', 'error'],
452
+ message: 'A rejected CommitRecord requires rejection evidence',
453
+ });
454
+ }
455
+ if (record.status !== rejectedStatusSchema.value && record.receipt.error) {
456
+ context.addIssue({
457
+ code: 'custom',
458
+ path: ['receipt', 'error'],
459
+ message: 'An accepted CommitRecord cannot carry rejection evidence',
460
+ });
461
+ }
462
+ });
463
+ export type CommitRecord = z.infer<typeof commitRecordSchema>;
464
+
465
+ export const commitRecordWhereSchema = z.strictObject({
466
+ actorId: z.string().min(1).optional(),
467
+ status: z.union([queuedStatusSchema, confirmedStatusSchema, rejectedStatusSchema]).optional(),
468
+ });
469
+ export type CommitRecordWhere = z.infer<typeof commitRecordWhereSchema>;
470
+
471
+ export const commitRecordListOptionsSchema = z.strictObject({
472
+ where: commitRecordWhereSchema.optional(),
473
+ cursor: z.string().min(1).optional(),
474
+ limit: z.number().int().min(1).max(100).optional(),
475
+ });
476
+ export type CommitRecordListOptions = z.infer<typeof commitRecordListOptionsSchema>;
477
+
478
+ export const commitRecordListSchema = z.strictObject({
479
+ data: z.array(commitRecordSchema).readonly(),
480
+ nextCursor: z.string().min(1).nullable(),
481
+ });
482
+ export type CommitRecordList = z.infer<typeof commitRecordListSchema>;
@@ -44,6 +44,7 @@ export const errorEnvelopeSchema = z
44
44
  message: z.string(),
45
45
  doc_url: z.string().optional(),
46
46
  request_id: z.string().optional(),
47
+ event_id: z.string().optional(),
47
48
  errors: z.array(errorEnvelopeItemSchema).readonly().optional(),
48
49
  })
49
50
  .catchall(z.unknown());
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The resume position for `GET /v1/logs`.
3
3
  *
4
- * The feed merges two independently-sequenced sources — the settlement log and
4
+ * The feed merges two independently-sequenced sources — the commit log and
5
5
  * the claim-event log — so one opaque cursor has to carry two positions. They
6
6
  * cannot share a sequence: allocating claim positions from `next_sync_id` would
7
7
  * put ephemeral leases into the log that clients materialize rows from and that
@@ -15,8 +15,10 @@ import { z } from 'zod';
15
15
  // schema module to keep this file's runtime dependencies limited to Zod.
16
16
  import {
17
17
  commitOperationSchema as coordinationCommitOperationSchema,
18
- readDependencySchema,
19
- trackDependencySchema,
18
+ MAX_READ_SET_ENTRIES,
19
+ readDependencyListSchema,
20
+ readSetProjectionEntryCount,
21
+ trackDependencyListSchema,
20
22
  } from '../coordination/schema.js';
21
23
  import type { MutationResultMessageWire } from './commit.js';
22
24
 
@@ -56,8 +58,11 @@ export const commitOperationSchema = wireCommitOperationSchema;
56
58
  export const commitPayloadSchema = z.object({
57
59
  operations: z.array(wireCommitOperationSchema),
58
60
  clientTxId: z.string(),
59
- reads: z.array(readDependencySchema).nullish(),
60
- track: z.array(trackDependencySchema).nullish(),
61
+ reads: readDependencyListSchema.nullish(),
62
+ track: trackDependencyListSchema.nullish(),
63
+ }).refine((value) => readSetProjectionEntryCount(value) <= MAX_READ_SET_ENTRIES, {
64
+ path: ['reads'],
65
+ message: `reads and track may contain at most ${MAX_READ_SET_ENTRIES} entries combined`,
61
66
  });
62
67
 
63
68
  /** A client-to-server frame that asks the server to commit a batch atomically. */
package/src/wire/index.ts CHANGED
@@ -83,15 +83,21 @@ export type {
83
83
  MutationResultMessage,
84
84
  } from './frames.js';
85
85
 
86
- // Commit settlement backbone. The transport receipt, server execution cache,
86
+ // Commit lifecycle backbone. The transport receipt, server execution cache,
87
87
  // and normalized client acknowledgement are different envelopes composed from
88
- // this one discriminated settlement vocabulary.
88
+ // this one discriminated status vocabulary.
89
89
  export {
90
90
  COMMIT_CORRELATION_ID_MAX_LENGTH,
91
91
  correlationIdSchema,
92
+ commitTimestampSchema,
93
+ queuedStatusSchema,
94
+ confirmedStatusSchema,
95
+ rejectedStatusSchema,
96
+ queuedCommitStatusSchema,
97
+ confirmedCommitStatusSchema,
98
+ rejectedCommitStatusSchema,
92
99
  commitStatusSchema,
93
100
  commitWaitSchema,
94
- commitSettlementSchema,
95
101
  commitReceiptSchema,
96
102
  rejectedCommitReceiptSchema,
97
103
  mutationResultPayloadSchema,
@@ -102,15 +108,33 @@ export {
102
108
  // OpenAPI reference derives from rather than describing separately.
103
109
  commitOperationControlShape,
104
110
  commitOperationBodySchema,
111
+ commitActorSchema,
112
+ commitAttemptSchema,
113
+ commitClaimReferenceSchema,
114
+ commitRecordOperationSchema,
115
+ commitReceiptEvidenceSchema,
105
116
  commitRequestSchema,
117
+ commitRecordSchema,
118
+ commitRecordWhereSchema,
119
+ commitRecordListOptionsSchema,
120
+ commitRecordListSchema,
106
121
  } from './commit.js';
107
122
  export type {
108
123
  CommitOperationBody,
124
+ CommitActor,
125
+ CommitAttempt,
126
+ CommitClaimReference,
127
+ CommitRecordOperation,
128
+ CommitReceiptEvidence,
109
129
  CommitRequest,
130
+ CommitRecord,
131
+ CommitRecordWhere,
132
+ CommitRecordListOptions,
133
+ CommitRecordList,
110
134
  CorrelationId,
111
135
  CommitStatus,
136
+ CommitStatusValue,
112
137
  CommitWait,
113
- CommitSettlement,
114
138
  CommitReceiptWire,
115
139
  RejectedCommitReceiptWire,
116
140
  MutationResultPayload,
@@ -119,6 +143,10 @@ export type {
119
143
  MutationCommitResultInput,
120
144
  MutationCommitResult,
121
145
  } from './commit.js';
146
+ export {
147
+ effectiveAuthoritySchema,
148
+ } from '../auth/capability.js';
149
+ export type { EffectiveAuthority } from '../auth/capability.js';
122
150
 
123
151
  // The read-path delta contract: the shape the server broadcasts to clients as the
124
152
  // payload of a `delta` or `sync_response` frame, together with the shared
@@ -237,8 +265,13 @@ export type {
237
265
  export {
238
266
  modelReadResponseSchema,
239
267
  modelListResponseSchema,
268
+ modelListEvidenceSchema,
269
+ } from './modelResponses.js';
270
+ export type {
271
+ ModelReadResponse,
272
+ ModelListResponse,
273
+ ModelListEvidence,
240
274
  } from './modelResponses.js';
241
- export type { ModelReadResponse, ModelListResponse } from './modelResponses.js';
242
275
 
243
276
  // What a model is made of — the artifact's own field and relation shapes, which
244
277
  // the schema read reports and every other layer derives its types from.