@abloatai/transaction 0.47.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 (270) 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/branches.d.ts +61 -12
  32. package/dist/branches.d.ts.map +1 -1
  33. package/dist/branches.js +25 -3
  34. package/dist/branches.js.map +1 -1
  35. package/dist/commitRecordRuntime.d.ts +30 -0
  36. package/dist/commitRecordRuntime.d.ts.map +1 -0
  37. package/dist/commitRecordRuntime.js +188 -0
  38. package/dist/commitRecordRuntime.js.map +1 -0
  39. package/dist/coordination/index.d.ts +3 -3
  40. package/dist/coordination/index.d.ts.map +1 -1
  41. package/dist/coordination/index.js +2 -2
  42. package/dist/coordination/index.js.map +1 -1
  43. package/dist/coordination/locator.d.ts +5 -0
  44. package/dist/coordination/locator.d.ts.map +1 -1
  45. package/dist/coordination/locator.js +12 -0
  46. package/dist/coordination/locator.js.map +1 -1
  47. package/dist/coordination/schema.d.ts +205 -15
  48. package/dist/coordination/schema.d.ts.map +1 -1
  49. package/dist/coordination/schema.js +88 -47
  50. package/dist/coordination/schema.js.map +1 -1
  51. package/dist/coordination/targetConflict.js +1 -1
  52. package/dist/durableWrites.d.ts +3 -3
  53. package/dist/durableWrites.d.ts.map +1 -1
  54. package/dist/durableWrites.js +1 -1
  55. package/dist/errorCodes.d.ts +35 -2
  56. package/dist/errorCodes.d.ts.map +1 -1
  57. package/dist/errorCodes.js +67 -19
  58. package/dist/errorCodes.js.map +1 -1
  59. package/dist/errorObservation.d.ts +82 -0
  60. package/dist/errorObservation.d.ts.map +1 -0
  61. package/dist/errorObservation.js +101 -0
  62. package/dist/errorObservation.js.map +1 -0
  63. package/dist/errors.d.ts +2 -0
  64. package/dist/errors.d.ts.map +1 -1
  65. package/dist/errors.js +6 -1
  66. package/dist/errors.js.map +1 -1
  67. package/dist/footprint.d.ts +10 -22
  68. package/dist/footprint.d.ts.map +1 -1
  69. package/dist/footprint.js +12 -24
  70. package/dist/footprint.js.map +1 -1
  71. package/dist/index.d.ts +5 -2
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +1 -1
  74. package/dist/index.js.map +1 -1
  75. package/dist/internal/read-set.d.ts +8 -0
  76. package/dist/internal/read-set.d.ts.map +1 -0
  77. package/dist/internal/read-set.js +7 -0
  78. package/dist/internal/read-set.js.map +1 -0
  79. package/dist/logger.d.ts +1 -1
  80. package/dist/observability.d.ts +1 -1
  81. package/dist/observability.js +1 -1
  82. package/dist/readSetContext.d.ts +54 -0
  83. package/dist/readSetContext.d.ts.map +1 -0
  84. package/dist/readSetContext.js +98 -0
  85. package/dist/readSetContext.js.map +1 -0
  86. package/dist/resources/functionalUpdate.d.ts +13 -3
  87. package/dist/resources/functionalUpdate.d.ts.map +1 -1
  88. package/dist/resources/functionalUpdate.js.map +1 -1
  89. package/dist/resources/httpResources.d.ts +23 -7
  90. package/dist/resources/httpResources.d.ts.map +1 -1
  91. package/dist/resources/modelOperations.d.ts +2 -2
  92. package/dist/resources/modelOperations.js +1 -1
  93. package/dist/resources/mutationOptions.d.ts +1 -1
  94. package/dist/resources/mutationOptions.js +1 -1
  95. package/dist/resources/where.d.ts +1 -1
  96. package/dist/resources/where.js +1 -1
  97. package/dist/resources/writeOptionsSchema.d.ts +39 -4
  98. package/dist/resources/writeOptionsSchema.d.ts.map +1 -1
  99. package/dist/resources/writeOptionsSchema.js +12 -6
  100. package/dist/resources/writeOptionsSchema.js.map +1 -1
  101. package/dist/schema/index.js +1 -1
  102. package/dist/schema/index.js.map +1 -1
  103. package/dist/schema/openapi.d.ts.map +1 -1
  104. package/dist/schema/openapi.js +45 -11
  105. package/dist/schema/openapi.js.map +1 -1
  106. package/dist/server/commit.d.ts +7 -1
  107. package/dist/server/commit.d.ts.map +1 -1
  108. package/dist/server/index.d.ts +1 -1
  109. package/dist/server/index.d.ts.map +1 -1
  110. package/dist/server/index.js +1 -1
  111. package/dist/server/index.js.map +1 -1
  112. package/dist/source/adapters/kysely.d.ts.map +1 -1
  113. package/dist/source/adapters/kysely.js +11 -4
  114. package/dist/source/adapters/kysely.js.map +1 -1
  115. package/dist/source/adapters/kyselyMutationCore.d.ts.map +1 -1
  116. package/dist/source/adapters/kyselyMutationCore.js +14 -2
  117. package/dist/source/adapters/kyselyMutationCore.js.map +1 -1
  118. package/dist/source/contract.d.ts +2 -0
  119. package/dist/source/contract.d.ts.map +1 -1
  120. package/dist/source/index.d.ts +1 -1
  121. package/dist/source/index.d.ts.map +1 -1
  122. package/dist/source/types.d.ts +1 -1
  123. package/dist/testing/fixtures/httpResponses.d.ts +30 -1
  124. package/dist/testing/fixtures/httpResponses.d.ts.map +1 -1
  125. package/dist/testing/fixtures/httpResponses.js +48 -1
  126. package/dist/testing/fixtures/httpResponses.js.map +1 -1
  127. package/dist/transactionLayer.d.ts +1 -1
  128. package/dist/transactions/{settlement → confirmation}/commitEnvelope.d.ts +4 -2
  129. package/dist/transactions/confirmation/commitEnvelope.d.ts.map +1 -0
  130. package/dist/transactions/{settlement → confirmation}/commitEnvelope.js +8 -3
  131. package/dist/transactions/confirmation/commitEnvelope.js.map +1 -0
  132. package/dist/transactions/confirmation/httpCommitEnvelope.d.ts.map +1 -0
  133. package/dist/transactions/confirmation/httpCommitEnvelope.js.map +1 -0
  134. package/dist/transactions/confirmation/idempotencyKey.d.ts.map +1 -0
  135. package/dist/transactions/confirmation/idempotencyKey.js.map +1 -0
  136. package/dist/transactions/{settlement → confirmation}/pendingWrite.d.ts +3 -2
  137. package/dist/transactions/confirmation/pendingWrite.d.ts.map +1 -0
  138. package/dist/transactions/confirmation/pendingWrite.js.map +1 -0
  139. package/dist/transport/commitFrames.d.ts +1 -0
  140. package/dist/transport/commitFrames.d.ts.map +1 -1
  141. package/dist/transport/commitFrames.js +8 -7
  142. package/dist/transport/commitFrames.js.map +1 -1
  143. package/dist/transport/connectionManager.d.ts +1 -1
  144. package/dist/transport/connectionManager.js +1 -1
  145. package/dist/transport/httpClient.d.ts +23 -9
  146. package/dist/transport/httpClient.d.ts.map +1 -1
  147. package/dist/transport/httpClient.js +134 -15
  148. package/dist/transport/httpClient.js.map +1 -1
  149. package/dist/transport/httpOptions.d.ts +9 -1
  150. package/dist/transport/httpOptions.d.ts.map +1 -1
  151. package/dist/transport/httpTransport.d.ts +3 -0
  152. package/dist/transport/httpTransport.d.ts.map +1 -1
  153. package/dist/transport/httpTransport.js +109 -22
  154. package/dist/transport/httpTransport.js.map +1 -1
  155. package/dist/transport/wsFrameHandlers.d.ts +3 -1
  156. package/dist/transport/wsFrameHandlers.d.ts.map +1 -1
  157. package/dist/transport/wsFrameHandlers.js +38 -17
  158. package/dist/transport/wsFrameHandlers.js.map +1 -1
  159. package/dist/transport/wsTransport.d.ts +3 -0
  160. package/dist/transport/wsTransport.d.ts.map +1 -1
  161. package/dist/transport/wsTransport.js +32 -1
  162. package/dist/transport/wsTransport.js.map +1 -1
  163. package/dist/utils/json.d.ts +1 -1
  164. package/dist/utils/json.js +1 -1
  165. package/dist/wire/accountResponses.d.ts +1 -1
  166. package/dist/wire/auth.d.ts +2 -2
  167. package/dist/wire/commit.d.ts +1652 -213
  168. package/dist/wire/commit.d.ts.map +1 -1
  169. package/dist/wire/commit.js +196 -132
  170. package/dist/wire/commit.js.map +1 -1
  171. package/dist/wire/delta.d.ts +1 -1
  172. package/dist/wire/errorEnvelope.d.ts +1 -0
  173. package/dist/wire/errorEnvelope.d.ts.map +1 -1
  174. package/dist/wire/errorEnvelope.js +1 -0
  175. package/dist/wire/errorEnvelope.js.map +1 -1
  176. package/dist/wire/feedCursor.d.ts +1 -1
  177. package/dist/wire/feedCursor.js +1 -1
  178. package/dist/wire/feedEvent.d.ts +3 -3
  179. package/dist/wire/frames.d.ts +8 -4
  180. package/dist/wire/frames.d.ts.map +1 -1
  181. package/dist/wire/frames.js +6 -3
  182. package/dist/wire/frames.js.map +1 -1
  183. package/dist/wire/inboundFrames.d.ts +44 -4
  184. package/dist/wire/inboundFrames.d.ts.map +1 -1
  185. package/dist/wire/index.d.ts +6 -4
  186. package/dist/wire/index.d.ts.map +1 -1
  187. package/dist/wire/index.js +6 -5
  188. package/dist/wire/index.js.map +1 -1
  189. package/dist/wire/modelMutations.d.ts +35 -0
  190. package/dist/wire/modelMutations.d.ts.map +1 -1
  191. package/dist/wire/modelMutations.js +9 -2
  192. package/dist/wire/modelMutations.js.map +1 -1
  193. package/dist/wire/modelResponses.d.ts +12 -1
  194. package/dist/wire/modelResponses.d.ts.map +1 -1
  195. package/dist/wire/modelResponses.js +12 -1
  196. package/dist/wire/modelResponses.js.map +1 -1
  197. package/package.json +12 -2
  198. package/src/ai-sdk/modelTools.ts +1 -1
  199. package/src/ai-sdk/updateTool.ts +2 -2
  200. package/src/auth/bootstrapScope.ts +1 -1
  201. package/src/auth/capability.ts +13 -4
  202. package/src/auth/capabilityLifecycle.ts +3 -2
  203. package/src/auth/identity.ts +16 -1
  204. package/src/auth/index.ts +2 -0
  205. package/src/auth/schemas.ts +3 -0
  206. package/src/auth/sessionMint.ts +0 -1
  207. package/src/branches.ts +27 -3
  208. package/src/commitRecordRuntime.ts +245 -0
  209. package/src/coordination/index.ts +24 -1
  210. package/src/coordination/locator.ts +17 -0
  211. package/src/coordination/schema.ts +114 -49
  212. package/src/coordination/targetConflict.ts +1 -1
  213. package/src/durableWrites.ts +3 -3
  214. package/src/errorCodes.ts +205 -25
  215. package/src/errorObservation.ts +121 -0
  216. package/src/errors.ts +5 -1
  217. package/src/footprint.ts +16 -27
  218. package/src/index.ts +8 -1
  219. package/src/internal/read-set.ts +18 -0
  220. package/src/logger.ts +1 -1
  221. package/src/observability.ts +1 -1
  222. package/src/readSetContext.ts +164 -0
  223. package/src/resources/functionalUpdate.ts +16 -4
  224. package/src/resources/httpResources.ts +27 -8
  225. package/src/resources/modelOperations.ts +2 -2
  226. package/src/resources/mutationOptions.ts +1 -1
  227. package/src/resources/where.ts +1 -1
  228. package/src/resources/writeOptionsSchema.ts +30 -12
  229. package/src/schema/index.ts +1 -1
  230. package/src/schema/openapi.ts +52 -11
  231. package/src/server/commit.ts +7 -1
  232. package/src/server/index.ts +5 -1
  233. package/src/source/adapters/kysely.ts +12 -4
  234. package/src/source/adapters/kyselyMutationCore.ts +14 -2
  235. package/src/source/index.ts +1 -1
  236. package/src/source/types.ts +1 -1
  237. package/src/testing/fixtures/httpResponses.ts +60 -1
  238. package/src/transactionLayer.ts +1 -1
  239. package/src/transactions/{settlement → confirmation}/commitEnvelope.ts +13 -3
  240. package/src/transport/commitFrames.ts +7 -5
  241. package/src/transport/connectionManager.ts +1 -1
  242. package/src/transport/httpClient.ts +256 -29
  243. package/src/transport/httpOptions.ts +9 -1
  244. package/src/transport/httpTransport.ts +139 -27
  245. package/src/transport/wsFrameHandlers.ts +46 -8
  246. package/src/transport/wsTransport.ts +42 -1
  247. package/src/utils/json.ts +1 -1
  248. package/src/wire/commit.ts +248 -147
  249. package/src/wire/errorEnvelope.ts +1 -0
  250. package/src/wire/feedCursor.ts +1 -1
  251. package/src/wire/frames.ts +9 -4
  252. package/src/wire/index.ts +38 -5
  253. package/src/wire/modelMutations.ts +16 -2
  254. package/src/wire/modelResponses.ts +14 -1
  255. package/dist/transactions/settlement/commitEnvelope.d.ts.map +0 -1
  256. package/dist/transactions/settlement/commitEnvelope.js.map +0 -1
  257. package/dist/transactions/settlement/httpCommitEnvelope.d.ts.map +0 -1
  258. package/dist/transactions/settlement/httpCommitEnvelope.js.map +0 -1
  259. package/dist/transactions/settlement/idempotencyKey.d.ts.map +0 -1
  260. package/dist/transactions/settlement/idempotencyKey.js.map +0 -1
  261. package/dist/transactions/settlement/pendingWrite.d.ts.map +0 -1
  262. package/dist/transactions/settlement/pendingWrite.js.map +0 -1
  263. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.d.ts +0 -0
  264. /package/dist/transactions/{settlement → confirmation}/httpCommitEnvelope.js +0 -0
  265. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.d.ts +0 -0
  266. /package/dist/transactions/{settlement → confirmation}/idempotencyKey.js +0 -0
  267. /package/dist/transactions/{settlement → confirmation}/pendingWrite.js +0 -0
  268. /package/src/transactions/{settlement → confirmation}/httpCommitEnvelope.ts +0 -0
  269. /package/src/transactions/{settlement → confirmation}/idempotencyKey.ts +0 -0
  270. /package/src/transactions/{settlement → confirmation}/pendingWrite.ts +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  hasWireCode,
5
5
  translateHttpError,
6
6
  } from '../errors.js';
7
- import { capabilityScopeSchema } from './capability.js';
7
+ import { effectiveAuthoritySchema } from './capability.js';
8
8
  import type {
9
9
  RotateSessionParams,
10
10
  SessionRevocation,
@@ -27,7 +27,8 @@ export const capabilityRotationResponseSchema = z.object({
27
27
  token: z.string().trim().min(1),
28
28
  expiresAt: z.iso.datetime().nullable(),
29
29
  organizationId: z.string().min(1),
30
- scope: capabilityScopeSchema,
30
+ branchRoot: z.boolean().default(false),
31
+ scope: effectiveAuthoritySchema,
31
32
  rotatedFrom: z.object({
32
33
  capabilityId: z.string().min(1),
33
34
  expiresAt: z.iso.datetime(),
@@ -34,6 +34,7 @@ import type { AuthCredentialSource } from '../auth/credentialSource.js';
34
34
  import type { CredentialProvider } from './credentialResult.js';
35
35
  import { resolveApiKeyValue, resolveBootstrapBaseUrl } from './apiKey.js';
36
36
  import type { DeliveryPartitionRoute } from './deliveryPartition.js';
37
+ import type { EffectiveAuthority } from './capability.js';
37
38
 
38
39
  export interface IdentityResolveInput {
39
40
  readonly options: {
@@ -71,6 +72,8 @@ export interface ResolvedIdentity {
71
72
  readonly syncGroups: readonly string[] | undefined;
72
73
  readonly participantKind: ParticipantKind;
73
74
  readonly deliveryPartition: DeliveryPartitionRoute | null;
75
+ /** Effective authority confirmed by the server (or explicit self-hosted configuration). */
76
+ readonly authority: EffectiveAuthority;
74
77
  /** Set only on the hosted-cloud path; the caller keeps it to stop refreshes on shutdown. */
75
78
  readonly refreshScheduler: RefreshScheduler | null;
76
79
  }
@@ -195,6 +198,16 @@ export async function resolveParticipantIdentity(
195
198
  syncGroups: options.syncGroups,
196
199
  participantKind: kind,
197
200
  deliveryPartition: null,
201
+ authority: {
202
+ organizationId: accountScope,
203
+ projectId: accountScope,
204
+ branchId: internalOptions.branchId ?? null,
205
+ participantKind: kind,
206
+ participantId: userId,
207
+ operations: [],
208
+ syncGroups: options.syncGroups ?? [],
209
+ deliveryPartition: null,
210
+ },
198
211
  refreshScheduler: null,
199
212
  };
200
213
  }
@@ -246,6 +259,7 @@ async function resolveViaIdentity(
246
259
  syncGroups: mergedSyncGroups,
247
260
  participantKind: identity.participantKind,
248
261
  deliveryPartition: identity.deliveryPartition,
262
+ authority: identity.authority,
249
263
  refreshScheduler: null,
250
264
  };
251
265
  }
@@ -322,7 +336,7 @@ async function resolveHosted(input: HostedInput): Promise<ResolvedIdentity> {
322
336
  accountScope: exchange.scope.organizationId,
323
337
  projectId: exchange.scope.projectId ?? exchange.scope.organizationId,
324
338
  branchId: exchange.scope.branchId,
325
- branchRoot: exchange.scope.branchRoot,
339
+ branchRoot: exchange.branchRoot,
326
340
  // teamIds isn't needed because the server already encoded
327
341
  // team-level access into scope.syncGroups.
328
342
  teamIds: undefined,
@@ -330,6 +344,7 @@ async function resolveHosted(input: HostedInput): Promise<ResolvedIdentity> {
330
344
  syncGroups: exchange.scope.syncGroups,
331
345
  participantKind: input.kind,
332
346
  deliveryPartition: exchange.scope.deliveryPartition,
347
+ authority: exchange.scope,
333
348
  refreshScheduler,
334
349
  };
335
350
  }
package/src/auth/index.ts CHANGED
@@ -74,6 +74,7 @@ export {
74
74
  capabilityOperationSchema,
75
75
  capabilityCanSchema,
76
76
  capabilityCanSchemaFor,
77
+ effectiveAuthoritySchema,
77
78
  capabilityScopeSchema,
78
79
  capabilityRequestSchema,
79
80
  capabilityMintResponseSchema,
@@ -88,6 +89,7 @@ export type {
88
89
  CapabilityOperation,
89
90
  CapabilityCan,
90
91
  CapabilityGrant,
92
+ EffectiveAuthority,
91
93
  CapabilityScope,
92
94
  CapabilityMintResponse,
93
95
  CapabilityRequest,
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { participantKindSchema } from '../coordination/schema.js';
3
3
  import {
4
4
  capabilityMintResponseSchema,
5
+ effectiveAuthoritySchema,
5
6
  grantedOperationSchema,
6
7
  type CapabilityMintResponse,
7
8
  } from './capability.js';
@@ -26,6 +27,8 @@ export const IdentityResolveResponseSchema = z.object({
26
27
  branchRoot: z.boolean().default(false),
27
28
  syncGroups: z.array(z.string()),
28
29
  deliveryPartition: deliveryPartitionRouteSchema.nullable().default(null),
30
+ /** Canonical server-confirmed authority of the authenticated credential. */
31
+ authority: effectiveAuthoritySchema,
29
32
  userMeta: z.record(z.string(), z.unknown()),
30
33
  });
31
34
 
@@ -93,7 +93,6 @@ export async function mintSession<S extends SchemaRecord>(
93
93
  organizationId: res.organizationId,
94
94
  projectId: res.projectId,
95
95
  branchId: res.branchId,
96
- branchRoot: res.branchRoot,
97
96
  syncGroups: res.syncGroups,
98
97
  operations: res.operations,
99
98
  participantKind: 'user',
package/src/branches.ts CHANGED
@@ -87,6 +87,28 @@ export const branchParentCompatibilitySchema = z.enum([
87
87
  ]);
88
88
  export type BranchParentCompatibility = z.infer<typeof branchParentCompatibilitySchema>;
89
89
 
90
+ /**
91
+ * The one ownership/storage vocabulary for durable branch data.
92
+ * Missing configuration is explicit (`unbound`), never inferred as hosted.
93
+ */
94
+ export const branchStorageSchema = z.discriminatedUnion('kind', [
95
+ z.object({ kind: z.literal('unbound') }).strict(),
96
+ z.object({
97
+ kind: z.literal('customer'),
98
+ transport: z.enum(['direct', 'endpoint']),
99
+ status: z.enum(['unverified', 'active', 'rejected']),
100
+ }).strict(),
101
+ z.object({
102
+ kind: z.literal('internal'),
103
+ implementation: z.enum(['log', 'tables']),
104
+ }).strict(),
105
+ z.object({
106
+ kind: z.literal('blocked'),
107
+ reason: z.literal('orphaned_external_marker'),
108
+ }).strict(),
109
+ ]);
110
+ export type BranchStorage = z.infer<typeof branchStorageSchema>;
111
+
90
112
  export const branchStatusSchemaSummarySchema = z
91
113
  .object({
92
114
  active: z.boolean(),
@@ -101,8 +123,8 @@ export const branchStatusSchemaSummarySchema = z
101
123
 
102
124
  export const branchStatusDataSourceSchema = z
103
125
  .object({
104
- kind: z.enum(['hosted', 'direct', 'endpoint']),
105
- status: z.enum(['unverified', 'active', 'rejected']).nullable(),
126
+ connection: z.enum(['direct', 'endpoint']),
127
+ status: z.enum(['unverified', 'active', 'rejected']),
106
128
  host: z.string().nullable(),
107
129
  database: z.string().nullable(),
108
130
  cursor: z.string().nullable(),
@@ -111,7 +133,8 @@ export const branchStatusDataSourceSchema = z
111
133
  last_success_at: z.string().nullable(),
112
134
  last_error: z.string().nullable(),
113
135
  })
114
- .strict();
136
+ .strict()
137
+ .nullable();
115
138
 
116
139
  export const branchStatusBlockerSchema = z
117
140
  .object({
@@ -127,6 +150,7 @@ export const branchStatusResponseSchema = z
127
150
  branch: branchResponseSchema,
128
151
  ready: z.boolean(),
129
152
  schema: branchStatusSchemaSummarySchema,
153
+ storage: branchStorageSchema,
130
154
  data_source: branchStatusDataSourceSchema,
131
155
  blockers: z.array(branchStatusBlockerSchema).readonly(),
132
156
  })
@@ -0,0 +1,245 @@
1
+ import {
2
+ DEFAULT_TRACK_ON_STALE,
3
+ type CommitReadSetEntry,
4
+ type ReadDependency,
5
+ type ReadSetEntry,
6
+ type TrackDependency,
7
+ } from './coordination/schema.js';
8
+ import {
9
+ commitRecordIdentity,
10
+ publishCommitRecord,
11
+ type ReadSetContext,
12
+ } from './readSetContext.js';
13
+ import {
14
+ commitRecordSchema,
15
+ type CommitOperationBody,
16
+ type CommitReceiptWire,
17
+ } from './wire/commit.js';
18
+
19
+ interface HttpCommitObservation {
20
+ readonly receipt: CommitReceiptWire;
21
+ readonly method: string;
22
+ readonly path: string;
23
+ readonly body: unknown;
24
+ }
25
+
26
+ interface WebSocketCommitObservation {
27
+ readonly receipt: CommitReceiptWire;
28
+ readonly operations: readonly {
29
+ readonly type: string;
30
+ readonly model: string;
31
+ readonly id: string;
32
+ readonly input?: Record<string, unknown>;
33
+ readonly claimId?: string | null;
34
+ readonly readAt?: number | null;
35
+ readonly onStale?: CommitOperationBody['onStale'] | null;
36
+ readonly fenceToken?: number | null;
37
+ }[];
38
+ readonly reads?: readonly ReadDependency[] | null;
39
+ readonly track?: readonly TrackDependency[] | null;
40
+ }
41
+
42
+ function commitEntry(dependency: ReadDependency): CommitReadSetEntry {
43
+ return {
44
+ target: 'group' in dependency
45
+ ? { scope: 'group', group: dependency.group }
46
+ : {
47
+ scope: 'row',
48
+ model: dependency.model,
49
+ id: dependency.id,
50
+ ...(dependency.fields ? { fields: dependency.fields } : {}),
51
+ },
52
+ watermark: dependency.readAt,
53
+ lifetime: 'commit',
54
+ onStale: dependency.onStale ?? 'reject',
55
+ };
56
+ }
57
+
58
+ function persistedEntry(
59
+ dependency: TrackDependency,
60
+ defaultWatermark: number,
61
+ ): ReadSetEntry {
62
+ return {
63
+ target: 'group' in dependency
64
+ ? { scope: 'group', group: dependency.group }
65
+ : { scope: 'row', model: dependency.model, id: dependency.id },
66
+ watermark: dependency.readAt ?? defaultWatermark,
67
+ lifetime: 'persisted',
68
+ onStale: dependency.onStale ?? DEFAULT_TRACK_ON_STALE,
69
+ };
70
+ }
71
+
72
+ function bodyRecord(value: unknown): Record<string, unknown> {
73
+ return typeof value === 'object' && value !== null
74
+ ? value as Record<string, unknown>
75
+ : {};
76
+ }
77
+
78
+ function retainedOperations(operations: readonly CommitOperationBody[]) {
79
+ return operations.map(({ data: _data, ...operation }) => ({
80
+ ...operation,
81
+ data: { retention: 'redacted' as const },
82
+ }));
83
+ }
84
+
85
+ function commitEvidence(
86
+ receipt: CommitReceiptWire,
87
+ operations: readonly CommitOperationBody[],
88
+ transport: 'http' | 'websocket',
89
+ ) {
90
+ return {
91
+ attempts: [{
92
+ id: receipt.clientTxId,
93
+ observedAt: receipt.createdAt,
94
+ transport,
95
+ kind: 'execution' as const,
96
+ }],
97
+ actor: {
98
+ kind: receipt.authority.participantKind,
99
+ id: receipt.authority.participantId,
100
+ },
101
+ // A receipt does not prove which live claim grant the server matched.
102
+ // Durable server records populate this from the claim gate instead.
103
+ claims: [],
104
+ };
105
+ }
106
+
107
+ function operationFromModelRequest(
108
+ observation: HttpCommitObservation,
109
+ body: Record<string, unknown>,
110
+ ): CommitOperationBody[] {
111
+ const segments = observation.path.split('/').filter(Boolean);
112
+ const modelIndex = segments.indexOf('models');
113
+ if (modelIndex < 0) return [];
114
+ const model = decodeURIComponent(segments[modelIndex + 1] ?? '');
115
+ const pathId = segments[modelIndex + 2];
116
+ const action = observation.method === 'POST'
117
+ ? 'create'
118
+ : observation.method === 'DELETE'
119
+ ? 'delete'
120
+ : 'update';
121
+ return [{
122
+ action,
123
+ model,
124
+ ...(pathId ? { id: decodeURIComponent(pathId) } : typeof body.id === 'string' ? { id: body.id } : {}),
125
+ ...(body.data && typeof body.data === 'object'
126
+ ? { data: body.data as Record<string, unknown> }
127
+ : {}),
128
+ ...(typeof body.readAt === 'number' ? { readAt: body.readAt } : {}),
129
+ ...(typeof body.onStale === 'string'
130
+ ? { onStale: body.onStale as CommitOperationBody['onStale'] }
131
+ : {}),
132
+ ...(typeof body.fenceToken === 'number' ? { fenceToken: body.fenceToken } : {}),
133
+ ...(typeof body.claim === 'string' ? { claimId: body.claim } : {}),
134
+ }];
135
+ }
136
+
137
+ /** @internal Joins exact HTTP request and receipt projections. */
138
+ export function recordHttpCommitReceipt(
139
+ context: ReadSetContext | undefined,
140
+ observation: HttpCommitObservation,
141
+ ): void {
142
+ if (!context?.getStore()) return;
143
+ const body = bodyRecord(observation.body);
144
+ const operations = Array.isArray(body.operations)
145
+ ? body.operations as CommitOperationBody[]
146
+ : operationFromModelRequest(observation, body);
147
+ const reads = Array.isArray(body.reads) ? body.reads as ReadDependency[] : [];
148
+ const track = Array.isArray(body.track) ? body.track as TrackDependency[] : [];
149
+ const readSet: ReadSetEntry[] = reads.map(commitEntry);
150
+ for (const operation of operations) {
151
+ if (typeof operation.readAt !== 'number' || !operation.id) continue;
152
+ readSet.push(commitEntry({
153
+ model: operation.model,
154
+ id: operation.id,
155
+ readAt: operation.readAt,
156
+ onStale: operation.onStale ?? 'reject',
157
+ }));
158
+ }
159
+ readSet.push(...track.map((entry) => persistedEntry(entry, observation.receipt.lastSyncId)));
160
+
161
+ const record = commitRecordSchema.parse({
162
+ id: commitRecordIdentity(context, observation.receipt.clientTxId).id,
163
+ createdAt: observation.receipt.createdAt,
164
+ status: observation.receipt.status,
165
+ statusAt: observation.receipt.statusAt,
166
+ lastSyncId: observation.receipt.lastSyncId,
167
+ ...(observation.receipt.correlationId
168
+ ? { correlationId: observation.receipt.correlationId }
169
+ : {}),
170
+ readSet,
171
+ operations: retainedOperations(operations),
172
+ ...commitEvidence(observation.receipt, operations, 'http'),
173
+ authority: observation.receipt.authority,
174
+ receipt: {
175
+ clientTxId: observation.receipt.clientTxId,
176
+ serverTxId: observation.receipt.serverTxId,
177
+ ops: observation.receipt.ops,
178
+ ...(observation.receipt.notifications
179
+ ? { notifications: observation.receipt.notifications }
180
+ : {}),
181
+ ...(observation.receipt.missingIds
182
+ ? { missingIds: observation.receipt.missingIds }
183
+ : {}),
184
+ },
185
+ });
186
+ publishCommitRecord(context, record);
187
+ }
188
+
189
+ /** @internal Joins an exact commit frame and its authoritative WS receipt. */
190
+ export function recordWebSocketCommitReceipt(
191
+ context: ReadSetContext | undefined,
192
+ observation: WebSocketCommitObservation,
193
+ ): void {
194
+ if (!context?.getStore()) return;
195
+ const operations: CommitOperationBody[] = observation.operations.map((operation) => ({
196
+ action: operation.type.toLowerCase() as CommitOperationBody['action'],
197
+ model: operation.model,
198
+ id: operation.id,
199
+ ...(operation.input ? { data: operation.input } : {}),
200
+ ...(operation.claimId ? { claimId: operation.claimId } : {}),
201
+ ...(typeof operation.readAt === 'number' ? { readAt: operation.readAt } : {}),
202
+ ...(operation.onStale ? { onStale: operation.onStale } : {}),
203
+ ...(typeof operation.fenceToken === 'number'
204
+ ? { fenceToken: operation.fenceToken }
205
+ : {}),
206
+ }));
207
+ const readSet: ReadSetEntry[] = (observation.reads ?? []).map(commitEntry);
208
+ for (const operation of operations) {
209
+ if (typeof operation.readAt !== 'number' || !operation.id) continue;
210
+ readSet.push(commitEntry({
211
+ model: operation.model,
212
+ id: operation.id,
213
+ readAt: operation.readAt,
214
+ onStale: operation.onStale ?? 'reject',
215
+ }));
216
+ }
217
+ readSet.push(...(observation.track ?? []).map((entry) =>
218
+ persistedEntry(entry, observation.receipt.lastSyncId)));
219
+ const record = commitRecordSchema.parse({
220
+ id: commitRecordIdentity(context, observation.receipt.clientTxId).id,
221
+ createdAt: observation.receipt.createdAt,
222
+ status: observation.receipt.status,
223
+ statusAt: observation.receipt.statusAt,
224
+ lastSyncId: observation.receipt.lastSyncId,
225
+ ...(observation.receipt.correlationId
226
+ ? { correlationId: observation.receipt.correlationId }
227
+ : {}),
228
+ readSet,
229
+ operations: retainedOperations(operations),
230
+ ...commitEvidence(observation.receipt, operations, 'websocket'),
231
+ authority: observation.receipt.authority,
232
+ receipt: {
233
+ clientTxId: observation.receipt.clientTxId,
234
+ serverTxId: observation.receipt.serverTxId,
235
+ ops: observation.receipt.ops,
236
+ ...(observation.receipt.notifications
237
+ ? { notifications: observation.receipt.notifications }
238
+ : {}),
239
+ ...(observation.receipt.missingIds
240
+ ? { missingIds: observation.receipt.missingIds }
241
+ : {}),
242
+ },
243
+ });
244
+ publishCommitRecord(context, record);
245
+ }
@@ -22,6 +22,17 @@ export {
22
22
  targetRefSchema,
23
23
  // Layer 3 — optimistic stale-context
24
24
  onStaleModeSchema,
25
+ MAX_READ_SET_ENTRIES,
26
+ readSetWatermarkSchema,
27
+ readSetRowTargetSchema,
28
+ readSetGroupTargetSchema,
29
+ commitReadSetTargetSchema,
30
+ persistedReadSetRowTargetSchema,
31
+ persistedReadSetTargetSchema,
32
+ commitReadSetEntrySchema,
33
+ persistedReadSetEntrySchema,
34
+ readSetEntrySchema,
35
+ readSetSchema,
25
36
  writeGuardSchema,
26
37
  staleNotificationSchema,
27
38
  rowStaleNotificationSchema,
@@ -29,7 +40,10 @@ export {
29
40
  stalePropagationSchema,
30
41
 
31
42
  readDependencySchema,
43
+ readDependencyListSchema,
44
+ readSetProjectionEntryCount,
32
45
  trackDependencySchema,
46
+ trackDependencyListSchema,
33
47
  // Layer 2 — pessimistic claim / claim-lease
34
48
  claimStatusSchema,
35
49
  wireClaimStatusSchema,
@@ -103,6 +117,15 @@ export type {
103
117
  TargetRef,
104
118
  // Layer 3 — optimistic stale-context
105
119
  OnStaleMode,
120
+ ReadSetWatermark,
121
+ ReadSetRowTarget,
122
+ ReadSetGroupTarget,
123
+ CommitReadSetTarget,
124
+ PersistedReadSetTarget,
125
+ CommitReadSetEntry,
126
+ PersistedReadSetEntry,
127
+ ReadSetEntry,
128
+ ReadSet,
106
129
  WriteGuard,
107
130
  StaleNotification,
108
131
  RowStaleNotification,
@@ -179,6 +202,7 @@ export type {
179
202
  } from './locator.js';
180
203
  export {
181
204
  batchFence,
205
+ claimIdFor,
182
206
  fenceTokenFor,
183
207
  isTargetTuple,
184
208
  subTarget,
@@ -186,4 +210,3 @@ export {
186
210
  modelTarget,
187
211
  streamTarget,
188
212
  } from './locator.js';
189
-
@@ -198,3 +198,20 @@ export function fenceTokenFor(
198
198
  ? fence.token
199
199
  : null;
200
200
  }
201
+
202
+ /**
203
+ * The claim identity an operation carries when it writes the row named by a
204
+ * held claim. Like the fence token, a claim id cannot travel to another row.
205
+ */
206
+ export function claimIdFor(
207
+ source: EntityLocator | null | undefined,
208
+ claimId: string | null | undefined,
209
+ model: string,
210
+ id: string | null,
211
+ ): string | null {
212
+ if (source == null || claimId == null || id === null) return null;
213
+ const target = modelTarget(source);
214
+ return model.toLowerCase() === target.model.toLowerCase() && id === target.id
215
+ ? claimId
216
+ : null;
217
+ }