@smthrs/sync 0.0.0-stage → 1.0.0-rc.4

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 (266) hide show
  1. package/CHANGELOG.md +208 -0
  2. package/LICENSE +21 -0
  3. package/README.md +208 -2
  4. package/dist/cjs/BranchCommands.d.ts +228 -0
  5. package/dist/cjs/BranchCommands.d.ts.map +1 -0
  6. package/dist/cjs/BranchCommands.js +316 -0
  7. package/dist/cjs/BranchCommands.js.map +7 -0
  8. package/dist/cjs/BranchIds.d.ts +65 -0
  9. package/dist/cjs/BranchIds.d.ts.map +1 -0
  10. package/dist/cjs/BranchIds.js +53 -0
  11. package/dist/cjs/BranchIds.js.map +7 -0
  12. package/dist/cjs/BranchPresence.d.ts +195 -0
  13. package/dist/cjs/BranchPresence.d.ts.map +1 -0
  14. package/dist/cjs/BranchPresence.js +276 -0
  15. package/dist/cjs/BranchPresence.js.map +7 -0
  16. package/dist/cjs/BranchProjection.d.ts +179 -0
  17. package/dist/cjs/BranchProjection.d.ts.map +1 -0
  18. package/dist/cjs/BranchProjection.js +197 -0
  19. package/dist/cjs/BranchProjection.js.map +7 -0
  20. package/dist/cjs/BranchProtocol.d.ts +415 -0
  21. package/dist/cjs/BranchProtocol.d.ts.map +1 -0
  22. package/dist/cjs/BranchProtocol.js +172 -0
  23. package/dist/cjs/BranchProtocol.js.map +7 -0
  24. package/dist/cjs/BranchRpcs.d.ts +177 -0
  25. package/dist/cjs/BranchRpcs.d.ts.map +1 -0
  26. package/dist/cjs/BranchRpcs.js +85 -0
  27. package/dist/cjs/BranchRpcs.js.map +7 -0
  28. package/dist/cjs/BranchServer.d.ts +55 -0
  29. package/dist/cjs/BranchServer.d.ts.map +1 -0
  30. package/dist/cjs/BranchServer.js +147 -0
  31. package/dist/cjs/BranchServer.js.map +7 -0
  32. package/dist/cjs/BranchShare.d.ts +159 -0
  33. package/dist/cjs/BranchShare.d.ts.map +1 -0
  34. package/dist/cjs/BranchShare.js +147 -0
  35. package/dist/cjs/BranchShare.js.map +7 -0
  36. package/dist/cjs/RunCatalog.d.ts +207 -0
  37. package/dist/cjs/RunCatalog.d.ts.map +1 -0
  38. package/dist/cjs/RunCatalog.js +122 -0
  39. package/dist/cjs/RunCatalog.js.map +7 -0
  40. package/dist/cjs/SyncAuth.d.ts +70 -0
  41. package/dist/cjs/SyncAuth.d.ts.map +1 -0
  42. package/dist/cjs/SyncAuth.js +109 -0
  43. package/dist/cjs/SyncAuth.js.map +7 -0
  44. package/dist/cjs/SyncClient.d.ts +274 -0
  45. package/dist/cjs/SyncClient.d.ts.map +1 -0
  46. package/dist/cjs/SyncClient.js +474 -0
  47. package/dist/cjs/SyncClient.js.map +7 -0
  48. package/dist/cjs/SyncError.d.ts +107 -0
  49. package/dist/cjs/SyncError.d.ts.map +1 -0
  50. package/dist/cjs/SyncError.js +110 -0
  51. package/dist/cjs/SyncError.js.map +7 -0
  52. package/dist/cjs/SyncPrincipal.d.ts +100 -0
  53. package/dist/cjs/SyncPrincipal.d.ts.map +1 -0
  54. package/dist/cjs/SyncPrincipal.js +48 -0
  55. package/dist/cjs/SyncPrincipal.js.map +7 -0
  56. package/dist/cjs/SyncProtocol.d.ts +592 -0
  57. package/dist/cjs/SyncProtocol.d.ts.map +1 -0
  58. package/dist/cjs/SyncProtocol.js +165 -0
  59. package/dist/cjs/SyncProtocol.js.map +7 -0
  60. package/dist/cjs/SyncRpcs.d.ts +92 -0
  61. package/dist/cjs/SyncRpcs.d.ts.map +1 -0
  62. package/dist/cjs/SyncRpcs.js +35 -0
  63. package/dist/cjs/SyncRpcs.js.map +7 -0
  64. package/dist/cjs/SyncServer.d.ts +184 -0
  65. package/dist/cjs/SyncServer.d.ts.map +1 -0
  66. package/dist/cjs/SyncServer.js +513 -0
  67. package/dist/cjs/SyncServer.js.map +7 -0
  68. package/dist/cjs/WorkspaceShare.d.ts +174 -0
  69. package/dist/cjs/WorkspaceShare.d.ts.map +1 -0
  70. package/dist/cjs/WorkspaceShare.js +149 -0
  71. package/dist/cjs/WorkspaceShare.js.map +7 -0
  72. package/dist/cjs/index.d.ts +91 -0
  73. package/dist/cjs/index.d.ts.map +1 -0
  74. package/dist/cjs/index.js +67 -0
  75. package/dist/cjs/index.js.map +7 -0
  76. package/dist/cjs/internal/Admission.d.ts +43 -0
  77. package/dist/cjs/internal/Admission.d.ts.map +1 -0
  78. package/dist/cjs/internal/Admission.js +90 -0
  79. package/dist/cjs/internal/Admission.js.map +7 -0
  80. package/dist/cjs/internal/CauseText.d.ts +55 -0
  81. package/dist/cjs/internal/CauseText.d.ts.map +1 -0
  82. package/dist/cjs/internal/CauseText.js +38 -0
  83. package/dist/cjs/internal/CauseText.js.map +7 -0
  84. package/dist/cjs/internal/JournalErrorCode.d.ts +23 -0
  85. package/dist/cjs/internal/JournalErrorCode.d.ts.map +1 -0
  86. package/dist/cjs/internal/JournalErrorCode.js +34 -0
  87. package/dist/cjs/internal/JournalErrorCode.js.map +7 -0
  88. package/dist/cjs/internal/PolicyOptions.d.ts +57 -0
  89. package/dist/cjs/internal/PolicyOptions.d.ts.map +1 -0
  90. package/dist/cjs/internal/PolicyOptions.js +62 -0
  91. package/dist/cjs/internal/PolicyOptions.js.map +7 -0
  92. package/dist/cjs/internal/ProtocolVersion.d.ts +17 -0
  93. package/dist/cjs/internal/ProtocolVersion.d.ts.map +1 -0
  94. package/dist/cjs/internal/ProtocolVersion.js +44 -0
  95. package/dist/cjs/internal/ProtocolVersion.js.map +7 -0
  96. package/dist/cjs/internal/ShareSigner.d.ts +173 -0
  97. package/dist/cjs/internal/ShareSigner.d.ts.map +1 -0
  98. package/dist/cjs/internal/ShareSigner.js +150 -0
  99. package/dist/cjs/internal/ShareSigner.js.map +7 -0
  100. package/dist/cjs/internal/SnapshotBoundary.d.ts +17 -0
  101. package/dist/cjs/internal/SnapshotBoundary.d.ts.map +1 -0
  102. package/dist/cjs/internal/SnapshotBoundary.js +97 -0
  103. package/dist/cjs/internal/SnapshotBoundary.js.map +7 -0
  104. package/dist/cjs/package.json +1 -0
  105. package/dist/cjs/test/TestSocket.d.ts +55 -0
  106. package/dist/cjs/test/TestSocket.d.ts.map +1 -0
  107. package/dist/cjs/test/TestSocket.js +145 -0
  108. package/dist/cjs/test/TestSocket.js.map +7 -0
  109. package/dist/cjs/test/TestSync.d.ts +54 -0
  110. package/dist/cjs/test/TestSync.d.ts.map +1 -0
  111. package/dist/cjs/test/TestSync.js +96 -0
  112. package/dist/cjs/test/TestSync.js.map +7 -0
  113. package/dist/esm/BranchCommands.d.ts +228 -0
  114. package/dist/esm/BranchCommands.d.ts.map +1 -0
  115. package/dist/esm/BranchCommands.js +496 -0
  116. package/dist/esm/BranchCommands.js.map +1 -0
  117. package/dist/esm/BranchIds.d.ts +65 -0
  118. package/dist/esm/BranchIds.d.ts.map +1 -0
  119. package/dist/esm/BranchIds.js +61 -0
  120. package/dist/esm/BranchIds.js.map +1 -0
  121. package/dist/esm/BranchPresence.d.ts +195 -0
  122. package/dist/esm/BranchPresence.d.ts.map +1 -0
  123. package/dist/esm/BranchPresence.js +361 -0
  124. package/dist/esm/BranchPresence.js.map +1 -0
  125. package/dist/esm/BranchProjection.d.ts +179 -0
  126. package/dist/esm/BranchProjection.d.ts.map +1 -0
  127. package/dist/esm/BranchProjection.js +269 -0
  128. package/dist/esm/BranchProjection.js.map +1 -0
  129. package/dist/esm/BranchProtocol.d.ts +415 -0
  130. package/dist/esm/BranchProtocol.d.ts.map +1 -0
  131. package/dist/esm/BranchProtocol.js +323 -0
  132. package/dist/esm/BranchProtocol.js.map +1 -0
  133. package/dist/esm/BranchRpcs.d.ts +177 -0
  134. package/dist/esm/BranchRpcs.d.ts.map +1 -0
  135. package/dist/esm/BranchRpcs.js +103 -0
  136. package/dist/esm/BranchRpcs.js.map +1 -0
  137. package/dist/esm/BranchServer.d.ts +55 -0
  138. package/dist/esm/BranchServer.d.ts.map +1 -0
  139. package/dist/esm/BranchServer.js +168 -0
  140. package/dist/esm/BranchServer.js.map +1 -0
  141. package/dist/esm/BranchShare.d.ts +159 -0
  142. package/dist/esm/BranchShare.d.ts.map +1 -0
  143. package/dist/esm/BranchShare.js +213 -0
  144. package/dist/esm/BranchShare.js.map +1 -0
  145. package/dist/esm/RunCatalog.d.ts +207 -0
  146. package/dist/esm/RunCatalog.d.ts.map +1 -0
  147. package/dist/esm/RunCatalog.js +196 -0
  148. package/dist/esm/RunCatalog.js.map +1 -0
  149. package/dist/esm/SyncAuth.d.ts +70 -0
  150. package/dist/esm/SyncAuth.d.ts.map +1 -0
  151. package/dist/esm/SyncAuth.js +119 -0
  152. package/dist/esm/SyncAuth.js.map +1 -0
  153. package/dist/esm/SyncClient.d.ts +274 -0
  154. package/dist/esm/SyncClient.d.ts.map +1 -0
  155. package/dist/esm/SyncClient.js +586 -0
  156. package/dist/esm/SyncClient.js.map +1 -0
  157. package/dist/esm/SyncError.d.ts +107 -0
  158. package/dist/esm/SyncError.d.ts.map +1 -0
  159. package/dist/esm/SyncError.js +133 -0
  160. package/dist/esm/SyncError.js.map +1 -0
  161. package/dist/esm/SyncPrincipal.d.ts +100 -0
  162. package/dist/esm/SyncPrincipal.d.ts.map +1 -0
  163. package/dist/esm/SyncPrincipal.js +58 -0
  164. package/dist/esm/SyncPrincipal.js.map +1 -0
  165. package/dist/esm/SyncProtocol.d.ts +592 -0
  166. package/dist/esm/SyncProtocol.d.ts.map +1 -0
  167. package/dist/esm/SyncProtocol.js +434 -0
  168. package/dist/esm/SyncProtocol.js.map +1 -0
  169. package/dist/esm/SyncRpcs.d.ts +92 -0
  170. package/dist/esm/SyncRpcs.d.ts.map +1 -0
  171. package/dist/esm/SyncRpcs.js +35 -0
  172. package/dist/esm/SyncRpcs.js.map +1 -0
  173. package/dist/esm/SyncServer.d.ts +184 -0
  174. package/dist/esm/SyncServer.d.ts.map +1 -0
  175. package/dist/esm/SyncServer.js +897 -0
  176. package/dist/esm/SyncServer.js.map +1 -0
  177. package/dist/esm/WorkspaceShare.d.ts +174 -0
  178. package/dist/esm/WorkspaceShare.d.ts.map +1 -0
  179. package/dist/esm/WorkspaceShare.js +208 -0
  180. package/dist/esm/WorkspaceShare.js.map +1 -0
  181. package/dist/esm/index.d.ts +91 -0
  182. package/dist/esm/index.d.ts.map +1 -0
  183. package/dist/esm/index.js +91 -0
  184. package/dist/esm/index.js.map +1 -0
  185. package/dist/esm/internal/Admission.d.ts +43 -0
  186. package/dist/esm/internal/Admission.d.ts.map +1 -0
  187. package/dist/esm/internal/Admission.js +80 -0
  188. package/dist/esm/internal/Admission.js.map +1 -0
  189. package/dist/esm/internal/CauseText.d.ts +55 -0
  190. package/dist/esm/internal/CauseText.d.ts.map +1 -0
  191. package/dist/esm/internal/CauseText.js +67 -0
  192. package/dist/esm/internal/CauseText.js.map +1 -0
  193. package/dist/esm/internal/JournalErrorCode.d.ts +23 -0
  194. package/dist/esm/internal/JournalErrorCode.d.ts.map +1 -0
  195. package/dist/esm/internal/JournalErrorCode.js +44 -0
  196. package/dist/esm/internal/JournalErrorCode.js.map +1 -0
  197. package/dist/esm/internal/PolicyOptions.d.ts +57 -0
  198. package/dist/esm/internal/PolicyOptions.d.ts.map +1 -0
  199. package/dist/esm/internal/PolicyOptions.js +70 -0
  200. package/dist/esm/internal/PolicyOptions.js.map +1 -0
  201. package/dist/esm/internal/ProtocolVersion.d.ts +17 -0
  202. package/dist/esm/internal/ProtocolVersion.d.ts.map +1 -0
  203. package/dist/esm/internal/ProtocolVersion.js +24 -0
  204. package/dist/esm/internal/ProtocolVersion.js.map +1 -0
  205. package/dist/esm/internal/ShareSigner.d.ts +173 -0
  206. package/dist/esm/internal/ShareSigner.d.ts.map +1 -0
  207. package/dist/esm/internal/ShareSigner.js +219 -0
  208. package/dist/esm/internal/ShareSigner.js.map +1 -0
  209. package/dist/esm/internal/SnapshotBoundary.d.ts +17 -0
  210. package/dist/esm/internal/SnapshotBoundary.d.ts.map +1 -0
  211. package/dist/esm/internal/SnapshotBoundary.js +68 -0
  212. package/dist/esm/internal/SnapshotBoundary.js.map +1 -0
  213. package/dist/esm/test/TestSocket.d.ts +55 -0
  214. package/dist/esm/test/TestSocket.d.ts.map +1 -0
  215. package/dist/esm/test/TestSocket.js +128 -0
  216. package/dist/esm/test/TestSocket.js.map +1 -0
  217. package/dist/esm/test/TestSync.d.ts +54 -0
  218. package/dist/esm/test/TestSync.d.ts.map +1 -0
  219. package/dist/esm/test/TestSync.js +76 -0
  220. package/dist/esm/test/TestSync.js.map +1 -0
  221. package/docs/README.md +156 -0
  222. package/docs/api.md +328 -0
  223. package/docs/concepts/authorization.md +117 -0
  224. package/docs/concepts/branches.md +195 -0
  225. package/docs/concepts/compaction.md +144 -0
  226. package/docs/concepts/replay-then-follow.md +129 -0
  227. package/docs/concepts/scopes-and-cursors.md +135 -0
  228. package/docs/guides/authorize-a-connection.md +174 -0
  229. package/docs/guides/follow-a-run.md +174 -0
  230. package/docs/guides/handle-a-compacted-run.md +158 -0
  231. package/docs/guides/list-workspace-runs.md +120 -0
  232. package/docs/guides/run-a-long-soak.md +80 -0
  233. package/docs/guides/serve-the-read-path.md +146 -0
  234. package/docs/guides/test-a-follower.md +273 -0
  235. package/docs/installation.md +92 -0
  236. package/docs/protocol.md +183 -0
  237. package/docs/quickstart.md +159 -0
  238. package/docs/troubleshooting.md +234 -0
  239. package/package.json +304 -3
  240. package/src/BranchCommands.ts +684 -0
  241. package/src/BranchIds.ts +76 -0
  242. package/src/BranchPresence.ts +512 -0
  243. package/src/BranchProjection.ts +308 -0
  244. package/src/BranchProtocol.ts +411 -0
  245. package/src/BranchRpcs.ts +163 -0
  246. package/src/BranchServer.ts +202 -0
  247. package/src/BranchShare.ts +297 -0
  248. package/src/RunCatalog.ts +308 -0
  249. package/src/SyncAuth.ts +146 -0
  250. package/src/SyncClient.ts +902 -0
  251. package/src/SyncError.ts +147 -0
  252. package/src/SyncPrincipal.ts +115 -0
  253. package/src/SyncProtocol.ts +594 -0
  254. package/src/SyncRpcs.ts +40 -0
  255. package/src/SyncServer.ts +1184 -0
  256. package/src/WorkspaceShare.ts +292 -0
  257. package/src/index.ts +107 -0
  258. package/src/internal/Admission.ts +119 -0
  259. package/src/internal/CauseText.ts +74 -0
  260. package/src/internal/JournalErrorCode.ts +46 -0
  261. package/src/internal/PolicyOptions.ts +97 -0
  262. package/src/internal/ProtocolVersion.ts +28 -0
  263. package/src/internal/ShareSigner.ts +300 -0
  264. package/src/internal/SnapshotBoundary.ts +91 -0
  265. package/src/test/TestSocket.ts +192 -0
  266. package/src/test/TestSync.ts +113 -0
@@ -0,0 +1,415 @@
1
+ /**
2
+ * The branch collaboration contract: a branch is one shared live document.
3
+ *
4
+ * A branch has no store of its own. Its durable state is exactly one journal
5
+ * run — `branchRunId` is the only mapping — so every replication guarantee the
6
+ * rest of this package already provides (canonical per-run `seq`, exclusive
7
+ * cursors, gap detection, resumable follow) carries over unchanged, and
8
+ * multiplayer never introduces a second source of truth.
9
+ *
10
+ * Ephemeral collaboration state (presence, cursors) deliberately has no
11
+ * representation here beyond the wire shapes: it is leased, never journalled.
12
+ *
13
+ * @since 0.1.0
14
+ */
15
+ import * as JournalEvent from "@smthrs/journal/JournalEvent";
16
+ import * as Schema from "effect/Schema";
17
+ /**
18
+ * Schema for the identifier of one collaboratively edited branch.
19
+ *
20
+ * @category schemas
21
+ * @since 0.1.0
22
+ */
23
+ export declare const BranchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
24
+ /**
25
+ * Identifier of one collaboratively edited branch.
26
+ *
27
+ * @category models
28
+ * @since 0.1.0
29
+ */
30
+ export type BranchId = typeof BranchId.Type;
31
+ /**
32
+ * Schema for the identifier of one connected client.
33
+ *
34
+ * @category schemas
35
+ * @since 0.1.0
36
+ */
37
+ export declare const ParticipantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
38
+ /**
39
+ * Identifier of one connected client.
40
+ *
41
+ * @category models
42
+ * @since 0.1.0
43
+ */
44
+ export type ParticipantId = typeof ParticipantId.Type;
45
+ /**
46
+ * Schema for a client-minted command identifier.
47
+ *
48
+ * The identifier is minted once per user intent and reused across optimistic
49
+ * application, retransmission, and concurrent submission, which is what makes
50
+ * it the idempotency key rather than a correlation id.
51
+ *
52
+ * @category schemas
53
+ * @since 0.1.0
54
+ */
55
+ export declare const CommandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
56
+ /**
57
+ * Client-minted command identifier used as the idempotency key.
58
+ *
59
+ * @category models
60
+ * @since 0.1.0
61
+ */
62
+ export type CommandId = typeof CommandId.Type;
63
+ /**
64
+ * Schema for the access a share capability grants.
65
+ *
66
+ * @category schemas
67
+ * @since 0.1.0
68
+ */
69
+ export declare const Access: Schema.Literals<readonly ["read", "write"]>;
70
+ /**
71
+ * The access a share capability grants.
72
+ *
73
+ * @category models
74
+ * @since 0.1.0
75
+ */
76
+ export type Access = typeof Access.Type;
77
+ /**
78
+ * The journal run backing a branch document.
79
+ *
80
+ * The prefix keeps branch runs from colliding with engine runs in the same
81
+ * workspace journal, and makes the mapping reversible for tooling.
82
+ *
83
+ * @category constructors
84
+ * @since 0.1.0
85
+ */
86
+ export declare const branchRunId: (branchId: BranchId) => JournalEvent.RunId;
87
+ /**
88
+ * The branch a journal run belongs to, or `null` for a non-branch run.
89
+ *
90
+ * The bare prefix is a non-branch run, not a branch with an empty id.
91
+ * `BranchId` is a branded `NonEmptyString`, so branding `""` would produce a
92
+ * value the brand forbids and hand it to `share.verify` as the branch under
93
+ * authorization. Refusing it here keeps the brand's promise true everywhere
94
+ * downstream of this function.
95
+ *
96
+ * @category constructors
97
+ * @since 0.1.0
98
+ */
99
+ export declare const branchOfRunId: (runId: JournalEvent.RunId) => BranchId | null;
100
+ /**
101
+ * The journal producer identity of one admitted command.
102
+ *
103
+ * `(runId, sourceId, sourceSeq)` is the journal's own durable idempotency
104
+ * key. Deriving the source from the client-minted `commandId` — with the
105
+ * fixed {@link commandSourceSeq} — turns that key into the branch's
106
+ * exactly-once admission constraint: two server instances racing the same
107
+ * command collide inside the journal's own transaction, so one appends and
108
+ * the other reads back the canonical sequence, instead of both appending
109
+ * (audit finding F-14). The branch run id already scopes the key, so equal
110
+ * command ids on different branches never collide.
111
+ *
112
+ * @category constructors
113
+ * @since 0.1.0
114
+ */
115
+ export declare const commandSourceId: (commandId: CommandId) => JournalEvent.SourceId;
116
+ /**
117
+ * The fixed producer sequence of an admitted command.
118
+ *
119
+ * A command's identity lives entirely in {@link commandSourceId}, so its
120
+ * producer sequence is always 0. It must be supplied explicitly: letting the
121
+ * journal allocate would hand a resubmission after a lost response sequence 1,
122
+ * a fresh identity, and therefore a second durable append.
123
+ *
124
+ * @category constructors
125
+ * @since 0.1.0
126
+ */
127
+ export declare const commandSourceSeq: JournalEvent.SourceSeq;
128
+ /**
129
+ * The single durable event type a branch document is built from.
130
+ *
131
+ * Chat, edits, and flow launches are all commands, so the branch journal has
132
+ * exactly one event shape and the projection has exactly one decoder.
133
+ *
134
+ * @category models
135
+ * @since 0.1.0
136
+ */
137
+ export declare const CommandEvent = "flows/branch/command";
138
+ declare const ShareClaims_base: Schema.Class<ShareClaims, Schema.Struct<{
139
+ readonly kid: Schema.NonEmptyString;
140
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
141
+ readonly capabilityId: Schema.NonEmptyString;
142
+ readonly access: Schema.Literals<readonly ["read", "write"]>;
143
+ readonly issuedAtMs: Schema.Int;
144
+ readonly expiresAtMs: Schema.Int;
145
+ }>, {}>;
146
+ /**
147
+ * Schema for the claims a share capability carries.
148
+ *
149
+ * `kid` names the key that signed the claims and is itself signed, so a
150
+ * verifier both selects the right key out of a rotating keyring and refuses a
151
+ * capability whose key name was swapped after minting. It matches
152
+ * `WorkspaceShare.WorkspaceClaims`: the two authorities are one scheme, and a
153
+ * branch link that could not survive a secret rotation was the only place they
154
+ * differed.
155
+ *
156
+ * @category schemas
157
+ * @since 0.1.0
158
+ */
159
+ export declare class ShareClaims extends ShareClaims_base {
160
+ }
161
+ declare const ShareCapability_base: Schema.Class<ShareCapability, Schema.Struct<{
162
+ readonly claims: typeof ShareClaims;
163
+ readonly signature: Schema.String;
164
+ }>, {}>;
165
+ /**
166
+ * Schema for a signed, expiring, branch-scoped share capability.
167
+ *
168
+ * @category schemas
169
+ * @since 0.1.0
170
+ */
171
+ export declare class ShareCapability extends ShareCapability_base {
172
+ }
173
+ declare const Cursor_base: Schema.Class<Cursor, Schema.Struct<{
174
+ readonly cardId: Schema.NonEmptyString;
175
+ readonly offset: Schema.Int;
176
+ }>, {}>;
177
+ /**
178
+ * Schema for a participant's caret inside one embedded card.
179
+ *
180
+ * @category schemas
181
+ * @since 0.1.0
182
+ */
183
+ export declare class Cursor extends Cursor_base {
184
+ }
185
+ /**
186
+ * BranchCard-compatible display coordinates; paths are metadata only.
187
+ *
188
+ * @category schemas
189
+ * @since 1.0.0-rc.1
190
+ */
191
+ export declare const PresenceWhere: Schema.Union<readonly [Schema.Struct<{
192
+ readonly kind: Schema.Literal<"file">;
193
+ readonly path: Schema.NonEmptyString;
194
+ readonly line: Schema.optionalKey<Schema.Int>;
195
+ }>, Schema.Struct<{
196
+ readonly kind: Schema.Literal<"terminal">;
197
+ readonly id: Schema.NonEmptyString;
198
+ }>, Schema.Struct<{
199
+ readonly kind: Schema.Literal<"step">;
200
+ readonly label: Schema.NonEmptyString;
201
+ }>, Schema.Struct<{
202
+ readonly kind: Schema.Literal<"branch">;
203
+ }>]>;
204
+ declare const Participant_base: Schema.Class<Participant, Schema.Struct<{
205
+ readonly leaseExpiresAtMs: Schema.Int;
206
+ readonly sessionId: Schema.optionalKey<Schema.NonEmptyString>;
207
+ readonly kind: Schema.optionalKey<Schema.Literals<readonly ["person", "agent"]>>;
208
+ readonly where: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
209
+ readonly kind: Schema.Literal<"file">;
210
+ readonly path: Schema.NonEmptyString;
211
+ readonly line: Schema.optionalKey<Schema.Int>;
212
+ }>, Schema.Struct<{
213
+ readonly kind: Schema.Literal<"terminal">;
214
+ readonly id: Schema.NonEmptyString;
215
+ }>, Schema.Struct<{
216
+ readonly kind: Schema.Literal<"step">;
217
+ readonly label: Schema.NonEmptyString;
218
+ }>, Schema.Struct<{
219
+ readonly kind: Schema.Literal<"branch">;
220
+ }>]>>;
221
+ readonly watching: Schema.optionalKey<Schema.NonEmptyString>;
222
+ readonly for_member: Schema.optionalKey<Schema.NonEmptyString>;
223
+ readonly agentKind: Schema.optionalKey<Schema.Literals<readonly ["smithers", "coding", "reviewer", "claude-code", "codex", "external"]>>;
224
+ readonly via: Schema.optionalKey<Schema.Literals<readonly ["ssh", "terminal", "cli"]>>;
225
+ readonly runId: Schema.optionalKey<Schema.NonEmptyString>;
226
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
227
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
228
+ readonly displayName: Schema.NonEmptyString;
229
+ readonly cursor: Schema.NullOr<typeof Cursor>;
230
+ }>, {}>;
231
+ /**
232
+ * Schema for one live participant on a branch.
233
+ *
234
+ * `leaseExpiresAtMs` is the whole lifetime contract: presence is a lease, so a
235
+ * client that disconnects without saying so disappears on its own.
236
+ *
237
+ * @category schemas
238
+ * @since 0.1.0
239
+ */
240
+ export declare class Participant extends Participant_base {
241
+ }
242
+ /**
243
+ * Schema for a capability-bearing request for one branch's roster.
244
+ *
245
+ * @category schemas
246
+ * @since 0.1.0
247
+ */
248
+ export declare const RosterRequest: Schema.Struct<{
249
+ readonly capability: typeof ShareCapability;
250
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
251
+ /** Only the authenticated host adapter consumes this readiness assertion. */
252
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
253
+ }>;
254
+ /**
255
+ * A capability-bearing request for one branch's roster.
256
+ *
257
+ * @category models
258
+ * @since 0.1.0
259
+ */
260
+ export type RosterRequest = typeof RosterRequest.Type;
261
+ /**
262
+ * Schema for a capability-bearing request to drop one participant.
263
+ *
264
+ * @category schemas
265
+ * @since 0.1.0
266
+ */
267
+ export declare const LeaveRequest: Schema.Struct<{
268
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
269
+ readonly sessionId: Schema.optionalKey<Schema.NonEmptyString>;
270
+ readonly capability: typeof ShareCapability;
271
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
272
+ /** Only the authenticated host adapter consumes this readiness assertion. */
273
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
274
+ }>;
275
+ /**
276
+ * A capability-bearing request to drop one participant.
277
+ *
278
+ * @category models
279
+ * @since 0.1.0
280
+ */
281
+ export type LeaveRequest = typeof LeaveRequest.Type;
282
+ /**
283
+ * Schema for one participant's announcement of itself on a branch.
284
+ *
285
+ * The same shape serves join, heartbeat, and cursor movement: an announcement
286
+ * is idempotent, so a client that reconnects simply announces again.
287
+ *
288
+ * @category schemas
289
+ * @since 0.1.0
290
+ */
291
+ export declare const Announcement: Schema.Struct<{
292
+ readonly capability: typeof ShareCapability;
293
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
294
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
295
+ readonly displayName: Schema.NonEmptyString;
296
+ readonly cursor: Schema.NullOr<typeof Cursor>;
297
+ readonly sessionId: Schema.optionalKey<Schema.NonEmptyString>;
298
+ readonly kind: Schema.optionalKey<Schema.Literals<readonly ["person", "agent"]>>;
299
+ readonly where: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
300
+ readonly kind: Schema.Literal<"file">;
301
+ readonly path: Schema.NonEmptyString;
302
+ readonly line: Schema.optionalKey<Schema.Int>;
303
+ }>, Schema.Struct<{
304
+ readonly kind: Schema.Literal<"terminal">;
305
+ readonly id: Schema.NonEmptyString;
306
+ }>, Schema.Struct<{
307
+ readonly kind: Schema.Literal<"step">;
308
+ readonly label: Schema.NonEmptyString;
309
+ }>, Schema.Struct<{
310
+ readonly kind: Schema.Literal<"branch">;
311
+ }>]>>;
312
+ readonly watching: Schema.optionalKey<Schema.NonEmptyString>;
313
+ readonly for_member: Schema.optionalKey<Schema.NonEmptyString>;
314
+ readonly agentKind: Schema.optionalKey<Schema.Literals<readonly ["smithers", "coding", "reviewer", "claude-code", "codex", "external"]>>;
315
+ readonly via: Schema.optionalKey<Schema.Literals<readonly ["ssh", "terminal", "cli"]>>;
316
+ readonly runId: Schema.optionalKey<Schema.NonEmptyString>;
317
+ }>;
318
+ /**
319
+ * One participant's announcement of itself on a branch.
320
+ *
321
+ * @category models
322
+ * @since 0.1.0
323
+ */
324
+ export type Announcement = typeof Announcement.Type;
325
+ declare const CommandSubmission_base: Schema.Class<CommandSubmission, Schema.Struct<{
326
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
327
+ readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
328
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
329
+ readonly name: Schema.NonEmptyString;
330
+ readonly args: Schema.String;
331
+ readonly target: Schema.String;
332
+ }>, {}>;
333
+ /**
334
+ * Schema for one submitted command or flow invocation.
335
+ *
336
+ * Every user action on a shared branch arrives here first — chat messages are
337
+ * the `branch.say` command, renames are `branch.rename`, and a flow launch is
338
+ * that flow's own name. `target` names the shared field a command durably
339
+ * edits and is `""` for commands that only append.
340
+ *
341
+ * @category schemas
342
+ * @since 0.1.0
343
+ */
344
+ export declare class CommandSubmission extends CommandSubmission_base {
345
+ }
346
+ /**
347
+ * Schema for a capability-bearing command submission.
348
+ *
349
+ * @category schemas
350
+ * @since 0.1.0
351
+ */
352
+ export declare const SubmitRequest: Schema.Struct<{
353
+ readonly capability: typeof ShareCapability;
354
+ readonly submission: typeof CommandSubmission;
355
+ }>;
356
+ /**
357
+ * A capability-bearing command submission.
358
+ *
359
+ * @category models
360
+ * @since 0.1.0
361
+ */
362
+ export type SubmitRequest = typeof SubmitRequest.Type;
363
+ declare const CommandEventPayload_base: Schema.Class<CommandEventPayload, Schema.Struct<{
364
+ readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
365
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
366
+ readonly name: Schema.NonEmptyString;
367
+ readonly args: Schema.withDecodingDefaultKey<Schema.String, never>;
368
+ readonly target: Schema.withDecodingDefaultKey<Schema.String, never>;
369
+ readonly capabilityId: Schema.withDecodingDefaultKey<Schema.String, never>;
370
+ }>, {}>;
371
+ /**
372
+ * The durable payload stored for an admitted command.
373
+ *
374
+ * Distinct from the wire request: `args` and `target` carry decoding defaults,
375
+ * so a row written before those fields existed still decodes rather than
376
+ * failing the whole ledger rebuild.
377
+ *
378
+ * `capabilityId` is the verified id of the share capability that submitted the
379
+ * command, stamped by `BranchCommands.submit` and never read from the client.
380
+ * `participantId` is whatever the client claimed, so attribute a command to
381
+ * its `capabilityId`. A row admitted before the field existed decodes with
382
+ * `""`, which no capability can carry.
383
+ *
384
+ * @category schemas
385
+ * @since 0.1.0
386
+ */
387
+ export declare class CommandEventPayload extends CommandEventPayload_base {
388
+ }
389
+ /**
390
+ * The command that appends a collaborative chat message.
391
+ *
392
+ * @category models
393
+ * @since 0.1.0
394
+ */
395
+ export declare const SayCommand = "branch.say";
396
+ declare const CommandReceipt_base: Schema.Class<CommandReceipt, Schema.Struct<{
397
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
398
+ readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
399
+ readonly status: Schema.Literals<readonly ["admitted", "duplicate"]>;
400
+ readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
401
+ }>, {}>;
402
+ /**
403
+ * Schema for the outcome of admitting a command.
404
+ *
405
+ * `duplicate` is a success, not a failure: it reports the canonical sequence
406
+ * the original submission already occupies so a retransmitting client can
407
+ * settle its optimistic row instead of executing the command twice.
408
+ *
409
+ * @category schemas
410
+ * @since 0.1.0
411
+ */
412
+ export declare class CommandReceipt extends CommandReceipt_base {
413
+ }
414
+ export {};
415
+ //# sourceMappingURL=BranchProtocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BranchProtocol.d.ts","sourceRoot":"","sources":["../../src/BranchProtocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAA;AAE5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,6EAAmF,CAAA;AAExG;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,aAAa,kFAAwF,CAAA;AAElH;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAErD;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,8EAAoF,CAAA;AAE1G;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,MAAM,6CAAqC,CAAA;AAExD;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AAEvC;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,QAAQ,KAAG,YAAY,CAAC,KACP,CAAA;AAKvD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,YAAY,CAAC,KAAK,KAAG,QAAQ,GAAG,IAIpE,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,GAAI,WAAW,SAAS,KAAG,YAAY,CAAC,QACN,CAAA;AAE9D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAAY,CAAC,SAAuC,CAAA;AAEnF;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,yBAAyB,CAAA;;;;;;;;;AAElD;;;;;;;;;;;;GAYG;AACH,qBAAa,WAAY,SAAQ,gBAO/B;CAAG;;;;;AAEL;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,oBAGnC;CAAG;;;;;AAEL;;;;;GAKG;AACH,qBAAa,MAAO,SAAQ,WAG1B;CAAG;AAEL;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;IASxB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBF;;;;;;;;GAQG;AACH,qBAAa,WAAY,SAAQ,gBAO/B;CAAG;AAEL;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;IAGxB,6EAA6E;;EAE7E,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAErD;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;IAlBvB,6EAA6E;;EAsB7E,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;AAEnD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;EAOvB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA;;;;;;;;;AAEnD;;;;;;;;;;GAUG;AACH,qBAAa,iBAAkB,SAAQ,sBASrC;CAAG;AAEL;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;EAAgF,CAAA;AAE1G;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;;;;;;;;;AAErD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAoB,SAAQ,wBASvC;CAAG;AAEL;;;;;GAKG;AACH,eAAO,MAAM,UAAU,eAAe,CAAA;;;;;;;AAEtC;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,mBAKlC;CAAG"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var BranchProtocol_exports = {};
30
+ __export(BranchProtocol_exports, {
31
+ Access: () => Access,
32
+ Announcement: () => Announcement,
33
+ BranchId: () => BranchId,
34
+ CommandEvent: () => CommandEvent,
35
+ CommandEventPayload: () => CommandEventPayload,
36
+ CommandId: () => CommandId,
37
+ CommandReceipt: () => CommandReceipt,
38
+ CommandSubmission: () => CommandSubmission,
39
+ Cursor: () => Cursor,
40
+ LeaveRequest: () => LeaveRequest,
41
+ Participant: () => Participant,
42
+ ParticipantId: () => ParticipantId,
43
+ PresenceWhere: () => PresenceWhere,
44
+ RosterRequest: () => RosterRequest,
45
+ SayCommand: () => SayCommand,
46
+ ShareCapability: () => ShareCapability,
47
+ ShareClaims: () => ShareClaims,
48
+ SubmitRequest: () => SubmitRequest,
49
+ branchOfRunId: () => branchOfRunId,
50
+ branchRunId: () => branchRunId,
51
+ commandSourceId: () => commandSourceId,
52
+ commandSourceSeq: () => commandSourceSeq
53
+ });
54
+ module.exports = __toCommonJS(BranchProtocol_exports);
55
+ var JournalEvent = __toESM(require("@smthrs/journal/JournalEvent"), 1);
56
+ var Effect = __toESM(require("effect/Effect"), 1);
57
+ var Schema = __toESM(require("effect/Schema"), 1);
58
+ const BranchId = Schema.NonEmptyString.pipe(Schema.brand("@smthrs/sync/BranchProtocol/BranchId"));
59
+ const ParticipantId = Schema.NonEmptyString.pipe(Schema.brand("@smthrs/sync/BranchProtocol/ParticipantId"));
60
+ const CommandId = Schema.NonEmptyString.pipe(Schema.brand("@smthrs/sync/BranchProtocol/CommandId"));
61
+ const Access = Schema.Literals(["read", "write"]);
62
+ const branchRunId = (branchId) => `${branchRunPrefix}${branchId}`;
63
+ const branchRunPrefix = "flows/branch/";
64
+ const branchOfRunId = (runId) => {
65
+ if (!runId.startsWith(branchRunPrefix)) return null;
66
+ const rest = runId.slice(branchRunPrefix.length);
67
+ return rest.length === 0 ? null : rest;
68
+ };
69
+ const commandSourceId = (commandId) => `flows/branch-command/${commandId}`;
70
+ const commandSourceSeq = 0;
71
+ const CommandEvent = "flows/branch/command";
72
+ class ShareClaims extends Schema.Class("@smthrs/sync/BranchProtocol/ShareClaims")({
73
+ kid: Schema.NonEmptyString,
74
+ branchId: BranchId,
75
+ capabilityId: Schema.NonEmptyString,
76
+ access: Access,
77
+ issuedAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
78
+ expiresAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
79
+ }) {
80
+ }
81
+ class ShareCapability extends Schema.Class("@smthrs/sync/BranchProtocol/ShareCapability")({
82
+ claims: ShareClaims,
83
+ signature: Schema.String
84
+ }) {
85
+ }
86
+ class Cursor extends Schema.Class("@smthrs/sync/BranchProtocol/Cursor")({
87
+ cardId: Schema.NonEmptyString,
88
+ offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
89
+ }) {
90
+ }
91
+ const PresenceWhere = Schema.Union([
92
+ Schema.Struct({
93
+ kind: Schema.Literal("file"),
94
+ path: Schema.NonEmptyString,
95
+ line: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0)))
96
+ }),
97
+ Schema.Struct({ kind: Schema.Literal("terminal"), id: Schema.NonEmptyString }),
98
+ Schema.Struct({ kind: Schema.Literal("step"), label: Schema.NonEmptyString }),
99
+ Schema.Struct({ kind: Schema.Literal("branch") })
100
+ ]);
101
+ const presenceFields = {
102
+ sessionId: Schema.optionalKey(Schema.NonEmptyString),
103
+ kind: Schema.optionalKey(Schema.Literals(["person", "agent"])),
104
+ where: Schema.optionalKey(PresenceWhere),
105
+ watching: Schema.optionalKey(Schema.NonEmptyString),
106
+ for_member: Schema.optionalKey(Schema.NonEmptyString),
107
+ agentKind: Schema.optionalKey(
108
+ Schema.Literals(["smithers", "coding", "reviewer", "claude-code", "codex", "external"])
109
+ ),
110
+ via: Schema.optionalKey(Schema.Literals(["ssh", "terminal", "cli"])),
111
+ runId: Schema.optionalKey(Schema.NonEmptyString)
112
+ };
113
+ class Participant extends Schema.Class("@smthrs/sync/BranchProtocol/Participant")({
114
+ branchId: BranchId,
115
+ participantId: ParticipantId,
116
+ displayName: Schema.NonEmptyString,
117
+ cursor: Schema.NullOr(Cursor),
118
+ ...presenceFields,
119
+ leaseExpiresAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
120
+ }) {
121
+ }
122
+ const RosterRequest = Schema.Struct({
123
+ capability: ShareCapability,
124
+ branchId: BranchId,
125
+ /** Only the authenticated host adapter consumes this readiness assertion. */
126
+ sourcesReady: Schema.optionalKey(Schema.Boolean)
127
+ });
128
+ const LeaveRequest = Schema.Struct({
129
+ ...RosterRequest.fields,
130
+ participantId: ParticipantId,
131
+ sessionId: Schema.optionalKey(Schema.NonEmptyString)
132
+ });
133
+ const Announcement = Schema.Struct({
134
+ ...presenceFields,
135
+ capability: ShareCapability,
136
+ branchId: BranchId,
137
+ participantId: ParticipantId,
138
+ displayName: Schema.NonEmptyString,
139
+ cursor: Schema.NullOr(Cursor)
140
+ });
141
+ class CommandSubmission extends Schema.Class(
142
+ "@smthrs/sync/BranchProtocol/CommandSubmission"
143
+ )({
144
+ branchId: BranchId,
145
+ commandId: CommandId,
146
+ participantId: ParticipantId,
147
+ name: Schema.NonEmptyString,
148
+ args: Schema.String,
149
+ target: Schema.String
150
+ }) {
151
+ }
152
+ const SubmitRequest = Schema.Struct({ capability: ShareCapability, submission: CommandSubmission });
153
+ class CommandEventPayload extends Schema.Class(
154
+ "@smthrs/sync/BranchProtocol/CommandEventPayload"
155
+ )({
156
+ commandId: CommandId,
157
+ participantId: ParticipantId,
158
+ name: Schema.NonEmptyString,
159
+ args: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(""))),
160
+ target: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(""))),
161
+ capabilityId: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed("")))
162
+ }) {
163
+ }
164
+ const SayCommand = "branch.say";
165
+ class CommandReceipt extends Schema.Class("@smthrs/sync/BranchProtocol/CommandReceipt")({
166
+ branchId: BranchId,
167
+ commandId: CommandId,
168
+ status: Schema.Literals(["admitted", "duplicate"]),
169
+ seq: JournalEvent.Seq
170
+ }) {
171
+ }
172
+ //# sourceMappingURL=BranchProtocol.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/BranchProtocol.ts"],
4
+ "sourcesContent": ["/**\n * The branch collaboration contract: a branch is one shared live document.\n *\n * A branch has no store of its own. Its durable state is exactly one journal\n * run \u2014 `branchRunId` is the only mapping \u2014 so every replication guarantee the\n * rest of this package already provides (canonical per-run `seq`, exclusive\n * cursors, gap detection, resumable follow) carries over unchanged, and\n * multiplayer never introduces a second source of truth.\n *\n * Ephemeral collaboration state (presence, cursors) deliberately has no\n * representation here beyond the wire shapes: it is leased, never journalled.\n *\n * @since 0.1.0\n */\n\nimport * as JournalEvent from \"@smthrs/journal/JournalEvent\"\nimport * as Effect from \"effect/Effect\"\nimport * as Schema from \"effect/Schema\"\n\n/**\n * Schema for the identifier of one collaboratively edited branch.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const BranchId = Schema.NonEmptyString.pipe(Schema.brand(\"@smthrs/sync/BranchProtocol/BranchId\"))\n\n/**\n * Identifier of one collaboratively edited branch.\n *\n * @category models\n * @since 0.1.0\n */\nexport type BranchId = typeof BranchId.Type\n\n/**\n * Schema for the identifier of one connected client.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const ParticipantId = Schema.NonEmptyString.pipe(Schema.brand(\"@smthrs/sync/BranchProtocol/ParticipantId\"))\n\n/**\n * Identifier of one connected client.\n *\n * @category models\n * @since 0.1.0\n */\nexport type ParticipantId = typeof ParticipantId.Type\n\n/**\n * Schema for a client-minted command identifier.\n *\n * The identifier is minted once per user intent and reused across optimistic\n * application, retransmission, and concurrent submission, which is what makes\n * it the idempotency key rather than a correlation id.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const CommandId = Schema.NonEmptyString.pipe(Schema.brand(\"@smthrs/sync/BranchProtocol/CommandId\"))\n\n/**\n * Client-minted command identifier used as the idempotency key.\n *\n * @category models\n * @since 0.1.0\n */\nexport type CommandId = typeof CommandId.Type\n\n/**\n * Schema for the access a share capability grants.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Access = Schema.Literals([\"read\", \"write\"])\n\n/**\n * The access a share capability grants.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Access = typeof Access.Type\n\n/**\n * The journal run backing a branch document.\n *\n * The prefix keeps branch runs from colliding with engine runs in the same\n * workspace journal, and makes the mapping reversible for tooling.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const branchRunId = (branchId: BranchId): JournalEvent.RunId =>\n `${branchRunPrefix}${branchId}` as JournalEvent.RunId\n\n/** The one prefix {@link branchRunId} writes and {@link branchOfRunId} reads. */\nconst branchRunPrefix = \"flows/branch/\"\n\n/**\n * The branch a journal run belongs to, or `null` for a non-branch run.\n *\n * The bare prefix is a non-branch run, not a branch with an empty id.\n * `BranchId` is a branded `NonEmptyString`, so branding `\"\"` would produce a\n * value the brand forbids and hand it to `share.verify` as the branch under\n * authorization. Refusing it here keeps the brand's promise true everywhere\n * downstream of this function.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const branchOfRunId = (runId: JournalEvent.RunId): BranchId | null => {\n if (!runId.startsWith(branchRunPrefix)) return null\n const rest = runId.slice(branchRunPrefix.length)\n return rest.length === 0 ? null : rest as BranchId\n}\n\n/**\n * The journal producer identity of one admitted command.\n *\n * `(runId, sourceId, sourceSeq)` is the journal's own durable idempotency\n * key. Deriving the source from the client-minted `commandId` \u2014 with the\n * fixed {@link commandSourceSeq} \u2014 turns that key into the branch's\n * exactly-once admission constraint: two server instances racing the same\n * command collide inside the journal's own transaction, so one appends and\n * the other reads back the canonical sequence, instead of both appending\n * (audit finding F-14). The branch run id already scopes the key, so equal\n * command ids on different branches never collide.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const commandSourceId = (commandId: CommandId): JournalEvent.SourceId =>\n `flows/branch-command/${commandId}` as JournalEvent.SourceId\n\n/**\n * The fixed producer sequence of an admitted command.\n *\n * A command's identity lives entirely in {@link commandSourceId}, so its\n * producer sequence is always 0. It must be supplied explicitly: letting the\n * journal allocate would hand a resubmission after a lost response sequence 1,\n * a fresh identity, and therefore a second durable append.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const commandSourceSeq: JournalEvent.SourceSeq = 0 as JournalEvent.SourceSeq\n\n/**\n * The single durable event type a branch document is built from.\n *\n * Chat, edits, and flow launches are all commands, so the branch journal has\n * exactly one event shape and the projection has exactly one decoder.\n *\n * @category models\n * @since 0.1.0\n */\nexport const CommandEvent = \"flows/branch/command\"\n\n/**\n * Schema for the claims a share capability carries.\n *\n * `kid` names the key that signed the claims and is itself signed, so a\n * verifier both selects the right key out of a rotating keyring and refuses a\n * capability whose key name was swapped after minting. It matches\n * `WorkspaceShare.WorkspaceClaims`: the two authorities are one scheme, and a\n * branch link that could not survive a secret rotation was the only place they\n * differed.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ShareClaims extends Schema.Class<ShareClaims>(\"@smthrs/sync/BranchProtocol/ShareClaims\")({\n kid: Schema.NonEmptyString,\n branchId: BranchId,\n capabilityId: Schema.NonEmptyString,\n access: Access,\n issuedAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),\n expiresAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))\n}) {}\n\n/**\n * Schema for a signed, expiring, branch-scoped share capability.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class ShareCapability extends Schema.Class<ShareCapability>(\"@smthrs/sync/BranchProtocol/ShareCapability\")({\n claims: ShareClaims,\n signature: Schema.String\n}) {}\n\n/**\n * Schema for a participant's caret inside one embedded card.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class Cursor extends Schema.Class<Cursor>(\"@smthrs/sync/BranchProtocol/Cursor\")({\n cardId: Schema.NonEmptyString,\n offset: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))\n}) {}\n\n/**\n * BranchCard-compatible display coordinates; paths are metadata only.\n *\n * @category schemas\n * @since 1.0.0-rc.1\n */\nexport const PresenceWhere = Schema.Union([\n Schema.Struct({\n kind: Schema.Literal(\"file\"),\n path: Schema.NonEmptyString,\n line: Schema.optionalKey(Schema.Int.check(Schema.isGreaterThan(0)))\n }),\n Schema.Struct({ kind: Schema.Literal(\"terminal\"), id: Schema.NonEmptyString }),\n Schema.Struct({ kind: Schema.Literal(\"step\"), label: Schema.NonEmptyString }),\n Schema.Struct({ kind: Schema.Literal(\"branch\") })\n])\n\n// Optional fields preserve decoding of recorded branch-protocol frames.\nconst presenceFields = {\n sessionId: Schema.optionalKey(Schema.NonEmptyString),\n kind: Schema.optionalKey(Schema.Literals([\"person\", \"agent\"])),\n where: Schema.optionalKey(PresenceWhere),\n watching: Schema.optionalKey(Schema.NonEmptyString),\n for_member: Schema.optionalKey(Schema.NonEmptyString),\n agentKind: Schema.optionalKey(\n Schema.Literals([\"smithers\", \"coding\", \"reviewer\", \"claude-code\", \"codex\", \"external\"])\n ),\n via: Schema.optionalKey(Schema.Literals([\"ssh\", \"terminal\", \"cli\"])),\n runId: Schema.optionalKey(Schema.NonEmptyString)\n}\n\n/**\n * Schema for one live participant on a branch.\n *\n * `leaseExpiresAtMs` is the whole lifetime contract: presence is a lease, so a\n * client that disconnects without saying so disappears on its own.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class Participant extends Schema.Class<Participant>(\"@smthrs/sync/BranchProtocol/Participant\")({\n branchId: BranchId,\n participantId: ParticipantId,\n displayName: Schema.NonEmptyString,\n cursor: Schema.NullOr(Cursor),\n ...presenceFields,\n leaseExpiresAtMs: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))\n}) {}\n\n/**\n * Schema for a capability-bearing request for one branch's roster.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const RosterRequest = Schema.Struct({\n capability: ShareCapability,\n branchId: BranchId,\n /** Only the authenticated host adapter consumes this readiness assertion. */\n sourcesReady: Schema.optionalKey(Schema.Boolean)\n})\n\n/**\n * A capability-bearing request for one branch's roster.\n *\n * @category models\n * @since 0.1.0\n */\nexport type RosterRequest = typeof RosterRequest.Type\n\n/**\n * Schema for a capability-bearing request to drop one participant.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const LeaveRequest = Schema.Struct({\n ...RosterRequest.fields,\n participantId: ParticipantId,\n sessionId: Schema.optionalKey(Schema.NonEmptyString)\n})\n\n/**\n * A capability-bearing request to drop one participant.\n *\n * @category models\n * @since 0.1.0\n */\nexport type LeaveRequest = typeof LeaveRequest.Type\n\n/**\n * Schema for one participant's announcement of itself on a branch.\n *\n * The same shape serves join, heartbeat, and cursor movement: an announcement\n * is idempotent, so a client that reconnects simply announces again.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const Announcement = Schema.Struct({\n ...presenceFields,\n capability: ShareCapability,\n branchId: BranchId,\n participantId: ParticipantId,\n displayName: Schema.NonEmptyString,\n cursor: Schema.NullOr(Cursor)\n})\n\n/**\n * One participant's announcement of itself on a branch.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Announcement = typeof Announcement.Type\n\n/**\n * Schema for one submitted command or flow invocation.\n *\n * Every user action on a shared branch arrives here first \u2014 chat messages are\n * the `branch.say` command, renames are `branch.rename`, and a flow launch is\n * that flow's own name. `target` names the shared field a command durably\n * edits and is `\"\"` for commands that only append.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class CommandSubmission extends Schema.Class<CommandSubmission>(\n \"@smthrs/sync/BranchProtocol/CommandSubmission\"\n)({\n branchId: BranchId,\n commandId: CommandId,\n participantId: ParticipantId,\n name: Schema.NonEmptyString,\n args: Schema.String,\n target: Schema.String\n}) {}\n\n/**\n * Schema for a capability-bearing command submission.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const SubmitRequest = Schema.Struct({ capability: ShareCapability, submission: CommandSubmission })\n\n/**\n * A capability-bearing command submission.\n *\n * @category models\n * @since 0.1.0\n */\nexport type SubmitRequest = typeof SubmitRequest.Type\n\n/**\n * The durable payload stored for an admitted command.\n *\n * Distinct from the wire request: `args` and `target` carry decoding defaults,\n * so a row written before those fields existed still decodes rather than\n * failing the whole ledger rebuild.\n *\n * `capabilityId` is the verified id of the share capability that submitted the\n * command, stamped by `BranchCommands.submit` and never read from the client.\n * `participantId` is whatever the client claimed, so attribute a command to\n * its `capabilityId`. A row admitted before the field existed decodes with\n * `\"\"`, which no capability can carry.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class CommandEventPayload extends Schema.Class<CommandEventPayload>(\n \"@smthrs/sync/BranchProtocol/CommandEventPayload\"\n)({\n commandId: CommandId,\n participantId: ParticipantId,\n name: Schema.NonEmptyString,\n args: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(\"\"))),\n target: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(\"\"))),\n capabilityId: Schema.String.pipe(Schema.withDecodingDefaultKey(Effect.succeed(\"\")))\n}) {}\n\n/**\n * The command that appends a collaborative chat message.\n *\n * @category models\n * @since 0.1.0\n */\nexport const SayCommand = \"branch.say\"\n\n/**\n * Schema for the outcome of admitting a command.\n *\n * `duplicate` is a success, not a failure: it reports the canonical sequence\n * the original submission already occupies so a retransmitting client can\n * settle its optimistic row instead of executing the command twice.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport class CommandReceipt extends Schema.Class<CommandReceipt>(\"@smthrs/sync/BranchProtocol/CommandReceipt\")({\n branchId: BranchId,\n commandId: CommandId,\n status: Schema.Literals([\"admitted\", \"duplicate\"]),\n seq: JournalEvent.Seq\n}) {}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }