@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,177 @@
1
+ /**
2
+ * The branch collaboration wire contract: the RPC projection of the write
3
+ * and presence paths that complement the read-only {@link SyncRpcs}.
4
+ *
5
+ * Authorization has two stages. Opening a branch is the bootstrap: no
6
+ * capability can exist yet, so `CreateBranch` requires an authenticated
7
+ * workspace principal supplied by the group's {@link SyncAuth} middleware,
8
+ * and its handler refuses any other principal.
9
+ *
10
+ * Every later procedure carries its own share capability and authorizes
11
+ * through {@link BranchShare}: past the bootstrap the capability IS the
12
+ * credential, and cross-branch or expired capabilities are refused by the
13
+ * same service boundary the in-process callers use.
14
+ *
15
+ * @since 0.1.0
16
+ */
17
+ import * as Schema from "effect/Schema";
18
+ import { Rpc, RpcGroup } from "effect/unstable/rpc";
19
+ import { CommandReceipt, Participant, ShareCapability } from "./BranchProtocol.ts";
20
+ import { SyncError } from "./SyncError.ts";
21
+ import { SyncAuth } from "./SyncRpcs.ts";
22
+ /**
23
+ * Maximum lifetime the branch bootstrap may mint.
24
+ *
25
+ * @category constants
26
+ * @since 0.1.0
27
+ */
28
+ export declare const maximumBranchTtlMs: number;
29
+ /**
30
+ * Schema for a request to open a new shared branch.
31
+ *
32
+ * The RPC authentication middleware must establish the workspace principal
33
+ * before the handler mints the first write capability.
34
+ *
35
+ * @category schemas
36
+ * @since 0.1.0
37
+ */
38
+ export declare const CreateBranchPayload: Schema.Struct<{
39
+ readonly ttlMs: Schema.Int;
40
+ }>;
41
+ /**
42
+ * A request to open a new shared branch.
43
+ *
44
+ * @category models
45
+ * @since 0.1.0
46
+ */
47
+ export type CreateBranchPayload = typeof CreateBranchPayload.Type;
48
+ /**
49
+ * Schema for the freshly opened branch and its owner capability.
50
+ *
51
+ * @category schemas
52
+ * @since 0.1.0
53
+ */
54
+ export declare const CreateBranchResponse: Schema.Struct<{
55
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
56
+ readonly capability: typeof ShareCapability;
57
+ }>;
58
+ /**
59
+ * The freshly opened branch and its owner capability.
60
+ *
61
+ * @category models
62
+ * @since 0.1.0
63
+ */
64
+ export type CreateBranchResponse = typeof CreateBranchResponse.Type;
65
+ /**
66
+ * Schema for a request to mint a share link for a branch.
67
+ *
68
+ * The presenting capability must hold write access to the branch — only a
69
+ * collaborator can invite one — and the minted link never outlives the
70
+ * capability it was minted from.
71
+ *
72
+ * @category schemas
73
+ * @since 0.1.0
74
+ */
75
+ export declare const MintSharePayload: Schema.Struct<{
76
+ readonly capability: typeof ShareCapability;
77
+ readonly access: Schema.Literals<readonly ["read", "write"]>;
78
+ readonly ttlMs: Schema.Int;
79
+ }>;
80
+ /**
81
+ * A request to mint a share link for a branch.
82
+ *
83
+ * @category models
84
+ * @since 0.1.0
85
+ */
86
+ export type MintSharePayload = typeof MintSharePayload.Type;
87
+ /**
88
+ * Schema for one roster emission on a branch watch stream.
89
+ *
90
+ * @category schemas
91
+ * @since 0.1.0
92
+ */
93
+ export declare const RosterFrame: Schema.Struct<{
94
+ readonly participants: Schema.$Array<typeof Participant>;
95
+ }>;
96
+ /**
97
+ * One roster emission on a branch watch stream.
98
+ *
99
+ * @category models
100
+ * @since 0.1.0
101
+ */
102
+ export type RosterFrame = typeof RosterFrame.Type;
103
+ /**
104
+ * The remote procedures of branch collaboration.
105
+ *
106
+ * The capability-bearing payloads ARE the `BranchProtocol` request schemas the
107
+ * branch services take, not copies of them. Four wire payloads used to
108
+ * re-declare the message their service already owned, and the announce copy
109
+ * had already drifted: it accepted `displayName: ""` where the service
110
+ * requires a non-empty name, so a wire-legal announce reached a `Participant`
111
+ * constructor and threw a defect outside the declared `SyncError` channel.
112
+ * Naming one schema on both sides makes that drift impossible rather than
113
+ * fixed once, and keeps this group free of the services themselves.
114
+ *
115
+ * @category groups
116
+ * @since 0.1.0
117
+ */
118
+ export declare const BranchRpcs: RpcGroup.RpcGroup<Rpc.Rpc<"Branch.CreateBranch", Schema.Struct<{
119
+ readonly ttlMs: Schema.Int;
120
+ }>, Schema.Struct<{
121
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
122
+ readonly capability: typeof ShareCapability;
123
+ }>, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.MintShare", Schema.Struct<{
124
+ readonly capability: typeof ShareCapability;
125
+ readonly access: Schema.Literals<readonly ["read", "write"]>;
126
+ readonly ttlMs: Schema.Int;
127
+ }>, typeof ShareCapability, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.Submit", Schema.Struct<{
128
+ readonly capability: typeof ShareCapability;
129
+ readonly submission: typeof import("./BranchProtocol.ts").CommandSubmission;
130
+ }>, typeof CommandReceipt, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.Announce", Schema.Struct<{
131
+ readonly capability: typeof ShareCapability;
132
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
133
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
134
+ readonly displayName: Schema.NonEmptyString;
135
+ readonly cursor: Schema.NullOr<typeof import("./BranchProtocol.ts").Cursor>;
136
+ readonly sessionId: Schema.optionalKey<Schema.NonEmptyString>;
137
+ readonly kind: Schema.optionalKey<Schema.Literals<readonly ["person", "agent"]>>;
138
+ readonly where: Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
139
+ readonly kind: Schema.Literal<"file">;
140
+ readonly path: Schema.NonEmptyString;
141
+ readonly line: Schema.optionalKey<Schema.Int>;
142
+ }>, Schema.Struct<{
143
+ readonly kind: Schema.Literal<"terminal">;
144
+ readonly id: Schema.NonEmptyString;
145
+ }>, Schema.Struct<{
146
+ readonly kind: Schema.Literal<"step">;
147
+ readonly label: Schema.NonEmptyString;
148
+ }>, Schema.Struct<{
149
+ readonly kind: Schema.Literal<"branch">;
150
+ }>]>>;
151
+ readonly watching: Schema.optionalKey<Schema.NonEmptyString>;
152
+ readonly for_member: Schema.optionalKey<Schema.NonEmptyString>;
153
+ readonly agentKind: Schema.optionalKey<Schema.Literals<readonly ["smithers", "coding", "reviewer", "claude-code", "codex", "external"]>>;
154
+ readonly via: Schema.optionalKey<Schema.Literals<readonly ["ssh", "terminal", "cli"]>>;
155
+ readonly runId: Schema.optionalKey<Schema.NonEmptyString>;
156
+ }>, typeof Participant, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.Leave", Schema.Struct<{
157
+ readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
158
+ readonly sessionId: Schema.optionalKey<Schema.NonEmptyString>;
159
+ readonly capability: typeof ShareCapability;
160
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
161
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
162
+ }>, Schema.Null, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.Roster", Schema.Struct<{
163
+ readonly capability: typeof ShareCapability;
164
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
165
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
166
+ }>, Schema.$Array<typeof Participant>, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.PresenceOn", Schema.Struct<{
167
+ readonly capability: typeof ShareCapability;
168
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
169
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
170
+ }>, Schema.Literals<readonly ["unknown", "present", "empty"]>, typeof SyncError, typeof SyncAuth, never> | Rpc.Rpc<"Branch.WatchRoster", Schema.Struct<{
171
+ readonly capability: typeof ShareCapability;
172
+ readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
173
+ readonly sourcesReady: Schema.optionalKey<Schema.Boolean>;
174
+ }>, import("effect/unstable/rpc/RpcSchema").Stream<Schema.Struct<{
175
+ readonly participants: Schema.$Array<typeof Participant>;
176
+ }>, typeof SyncError>, Schema.Never, typeof SyncAuth, never>>;
177
+ //# sourceMappingURL=BranchRpcs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BranchRpcs.d.ts","sourceRoot":"","sources":["../../src/BranchRpcs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAIL,cAAc,EAEd,WAAW,EAEX,eAAe,EAEhB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QAAsB,CAAA;AAErD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB;;EAK9B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,OAAO,mBAAmB,CAAC,IAAI,CAAA;AAEjE;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;EAG/B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,oBAAoB,CAAC,IAAI,CAAA;AAEnE;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB;;;;EAI3B,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,gBAAgB,CAAC,IAAI,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;EAEtB,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AAEjD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6DAqBD,CAAA"}
@@ -0,0 +1,85 @@
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 BranchRpcs_exports = {};
30
+ __export(BranchRpcs_exports, {
31
+ BranchRpcs: () => BranchRpcs,
32
+ CreateBranchPayload: () => CreateBranchPayload,
33
+ CreateBranchResponse: () => CreateBranchResponse,
34
+ MintSharePayload: () => MintSharePayload,
35
+ RosterFrame: () => RosterFrame,
36
+ maximumBranchTtlMs: () => maximumBranchTtlMs
37
+ });
38
+ module.exports = __toCommonJS(BranchRpcs_exports);
39
+ var Schema = __toESM(require("effect/Schema"), 1);
40
+ var import_rpc = require("effect/unstable/rpc");
41
+ var import_BranchProtocol = require("./BranchProtocol.js");
42
+ var import_SyncError = require("./SyncError.js");
43
+ var import_SyncRpcs = require("./SyncRpcs.js");
44
+ const maximumBranchTtlMs = 24 * 60 * 60 * 1e3;
45
+ const CreateBranchPayload = Schema.Struct({
46
+ ttlMs: Schema.Int.check(
47
+ Schema.isGreaterThan(0),
48
+ Schema.isLessThanOrEqualTo(maximumBranchTtlMs)
49
+ )
50
+ });
51
+ const CreateBranchResponse = Schema.Struct({
52
+ branchId: import_BranchProtocol.BranchId,
53
+ capability: import_BranchProtocol.ShareCapability
54
+ });
55
+ const MintSharePayload = Schema.Struct({
56
+ capability: import_BranchProtocol.ShareCapability,
57
+ access: import_BranchProtocol.Access,
58
+ ttlMs: Schema.Int.check(Schema.isGreaterThan(0))
59
+ });
60
+ const RosterFrame = Schema.Struct({
61
+ participants: Schema.Array(import_BranchProtocol.Participant)
62
+ });
63
+ const BranchRpcs = import_rpc.RpcGroup.make(
64
+ import_rpc.Rpc.make("Branch.CreateBranch", {
65
+ payload: CreateBranchPayload,
66
+ success: CreateBranchResponse,
67
+ error: import_SyncError.SyncError
68
+ }),
69
+ import_rpc.Rpc.make("Branch.MintShare", { payload: MintSharePayload, success: import_BranchProtocol.ShareCapability, error: import_SyncError.SyncError }),
70
+ import_rpc.Rpc.make("Branch.Submit", { payload: import_BranchProtocol.SubmitRequest, success: import_BranchProtocol.CommandReceipt, error: import_SyncError.SyncError }),
71
+ import_rpc.Rpc.make("Branch.Announce", { payload: import_BranchProtocol.Announcement, success: import_BranchProtocol.Participant, error: import_SyncError.SyncError }),
72
+ import_rpc.Rpc.make("Branch.Leave", { payload: import_BranchProtocol.LeaveRequest, success: Schema.Null, error: import_SyncError.SyncError }),
73
+ import_rpc.Rpc.make("Branch.Roster", {
74
+ payload: import_BranchProtocol.RosterRequest,
75
+ success: Schema.Array(import_BranchProtocol.Participant),
76
+ error: import_SyncError.SyncError
77
+ }),
78
+ import_rpc.Rpc.make("Branch.PresenceOn", {
79
+ payload: import_BranchProtocol.RosterRequest,
80
+ success: Schema.Literals(["unknown", "present", "empty"]),
81
+ error: import_SyncError.SyncError
82
+ }),
83
+ import_rpc.Rpc.make("Branch.WatchRoster", { payload: import_BranchProtocol.RosterRequest, success: RosterFrame, error: import_SyncError.SyncError, stream: true })
84
+ ).middleware(import_SyncRpcs.SyncAuth);
85
+ //# sourceMappingURL=BranchRpcs.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/BranchRpcs.ts"],
4
+ "sourcesContent": ["/**\n * The branch collaboration wire contract: the RPC projection of the write\n * and presence paths that complement the read-only {@link SyncRpcs}.\n *\n * Authorization has two stages. Opening a branch is the bootstrap: no\n * capability can exist yet, so `CreateBranch` requires an authenticated\n * workspace principal supplied by the group's {@link SyncAuth} middleware,\n * and its handler refuses any other principal.\n *\n * Every later procedure carries its own share capability and authorizes\n * through {@link BranchShare}: past the bootstrap the capability IS the\n * credential, and cross-branch or expired capabilities are refused by the\n * same service boundary the in-process callers use.\n *\n * @since 0.1.0\n */\n\nimport * as Schema from \"effect/Schema\"\nimport { Rpc, RpcGroup } from \"effect/unstable/rpc\"\nimport {\n Access,\n Announcement,\n BranchId,\n CommandReceipt,\n LeaveRequest,\n Participant,\n RosterRequest,\n ShareCapability,\n SubmitRequest\n} from \"./BranchProtocol.ts\"\nimport { SyncError } from \"./SyncError.ts\"\nimport { SyncAuth } from \"./SyncRpcs.ts\"\n\n/**\n * Maximum lifetime the branch bootstrap may mint.\n *\n * @category constants\n * @since 0.1.0\n */\nexport const maximumBranchTtlMs = 24 * 60 * 60 * 1000\n\n/**\n * Schema for a request to open a new shared branch.\n *\n * The RPC authentication middleware must establish the workspace principal\n * before the handler mints the first write capability.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const CreateBranchPayload = Schema.Struct({\n ttlMs: Schema.Int.check(\n Schema.isGreaterThan(0),\n Schema.isLessThanOrEqualTo(maximumBranchTtlMs)\n )\n})\n\n/**\n * A request to open a new shared branch.\n *\n * @category models\n * @since 0.1.0\n */\nexport type CreateBranchPayload = typeof CreateBranchPayload.Type\n\n/**\n * Schema for the freshly opened branch and its owner capability.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const CreateBranchResponse = Schema.Struct({\n branchId: BranchId,\n capability: ShareCapability\n})\n\n/**\n * The freshly opened branch and its owner capability.\n *\n * @category models\n * @since 0.1.0\n */\nexport type CreateBranchResponse = typeof CreateBranchResponse.Type\n\n/**\n * Schema for a request to mint a share link for a branch.\n *\n * The presenting capability must hold write access to the branch \u2014 only a\n * collaborator can invite one \u2014 and the minted link never outlives the\n * capability it was minted from.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const MintSharePayload = Schema.Struct({\n capability: ShareCapability,\n access: Access,\n ttlMs: Schema.Int.check(Schema.isGreaterThan(0))\n})\n\n/**\n * A request to mint a share link for a branch.\n *\n * @category models\n * @since 0.1.0\n */\nexport type MintSharePayload = typeof MintSharePayload.Type\n\n/**\n * Schema for one roster emission on a branch watch stream.\n *\n * @category schemas\n * @since 0.1.0\n */\nexport const RosterFrame = Schema.Struct({\n participants: Schema.Array(Participant)\n})\n\n/**\n * One roster emission on a branch watch stream.\n *\n * @category models\n * @since 0.1.0\n */\nexport type RosterFrame = typeof RosterFrame.Type\n\n/**\n * The remote procedures of branch collaboration.\n *\n * The capability-bearing payloads ARE the `BranchProtocol` request schemas the\n * branch services take, not copies of them. Four wire payloads used to\n * re-declare the message their service already owned, and the announce copy\n * had already drifted: it accepted `displayName: \"\"` where the service\n * requires a non-empty name, so a wire-legal announce reached a `Participant`\n * constructor and threw a defect outside the declared `SyncError` channel.\n * Naming one schema on both sides makes that drift impossible rather than\n * fixed once, and keeps this group free of the services themselves.\n *\n * @category groups\n * @since 0.1.0\n */\nexport const BranchRpcs = RpcGroup.make(\n Rpc.make(\"Branch.CreateBranch\", {\n payload: CreateBranchPayload,\n success: CreateBranchResponse,\n error: SyncError\n }),\n Rpc.make(\"Branch.MintShare\", { payload: MintSharePayload, success: ShareCapability, error: SyncError }),\n Rpc.make(\"Branch.Submit\", { payload: SubmitRequest, success: CommandReceipt, error: SyncError }),\n Rpc.make(\"Branch.Announce\", { payload: Announcement, success: Participant, error: SyncError }),\n Rpc.make(\"Branch.Leave\", { payload: LeaveRequest, success: Schema.Null, error: SyncError }),\n Rpc.make(\"Branch.Roster\", {\n payload: RosterRequest,\n success: Schema.Array(Participant),\n error: SyncError\n }),\n Rpc.make(\"Branch.PresenceOn\", {\n payload: RosterRequest,\n success: Schema.Literals([\"unknown\", \"present\", \"empty\"]),\n error: SyncError\n }),\n Rpc.make(\"Branch.WatchRoster\", { payload: RosterRequest, success: RosterFrame, error: SyncError, stream: true })\n).middleware(SyncAuth)\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,aAAwB;AACxB,iBAA8B;AAC9B,4BAUO;AACP,uBAA0B;AAC1B,sBAAyB;AAQlB,MAAM,qBAAqB,KAAK,KAAK,KAAK;AAW1C,MAAM,sBAAsB,OAAO,OAAO;AAAA,EAC/C,OAAO,OAAO,IAAI;AAAA,IAChB,OAAO,cAAc,CAAC;AAAA,IACtB,OAAO,oBAAoB,kBAAkB;AAAA,EAC/C;AACF,CAAC;AAgBM,MAAM,uBAAuB,OAAO,OAAO;AAAA,EAChD,UAAU;AAAA,EACV,YAAY;AACd,CAAC;AAoBM,MAAM,mBAAmB,OAAO,OAAO;AAAA,EAC5C,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,OAAO,OAAO,IAAI,MAAM,OAAO,cAAc,CAAC,CAAC;AACjD,CAAC;AAgBM,MAAM,cAAc,OAAO,OAAO;AAAA,EACvC,cAAc,OAAO,MAAM,iCAAW;AACxC,CAAC;AAyBM,MAAM,aAAa,oBAAS;AAAA,EACjC,eAAI,KAAK,uBAAuB;AAAA,IAC9B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,OAAO;AAAA,EACT,CAAC;AAAA,EACD,eAAI,KAAK,oBAAoB,EAAE,SAAS,kBAAkB,SAAS,uCAAiB,OAAO,2BAAU,CAAC;AAAA,EACtG,eAAI,KAAK,iBAAiB,EAAE,SAAS,qCAAe,SAAS,sCAAgB,OAAO,2BAAU,CAAC;AAAA,EAC/F,eAAI,KAAK,mBAAmB,EAAE,SAAS,oCAAc,SAAS,mCAAa,OAAO,2BAAU,CAAC;AAAA,EAC7F,eAAI,KAAK,gBAAgB,EAAE,SAAS,oCAAc,SAAS,OAAO,MAAM,OAAO,2BAAU,CAAC;AAAA,EAC1F,eAAI,KAAK,iBAAiB;AAAA,IACxB,SAAS;AAAA,IACT,SAAS,OAAO,MAAM,iCAAW;AAAA,IACjC,OAAO;AAAA,EACT,CAAC;AAAA,EACD,eAAI,KAAK,qBAAqB;AAAA,IAC5B,SAAS;AAAA,IACT,SAAS,OAAO,SAAS,CAAC,WAAW,WAAW,OAAO,CAAC;AAAA,IACxD,OAAO;AAAA,EACT,CAAC;AAAA,EACD,eAAI,KAAK,sBAAsB,EAAE,SAAS,qCAAe,SAAS,aAAa,OAAO,4BAAW,QAAQ,KAAK,CAAC;AACjH,EAAE,WAAW,wBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The server-side handlers of {@link BranchRpcs}: a thin projection of the
3
+ * branch services onto the wire.
4
+ *
5
+ * Five procedures forward to the service that owns their boundary and add
6
+ * nothing: `Branch.Submit` to {@link BranchCommands}, and `Branch.Announce`,
7
+ * `Branch.Leave`, `Branch.Roster`, and `Branch.WatchRoster` to
8
+ * {@link BranchPresence}. Two enforce policy here, on top of what
9
+ * {@link BranchShare} checks: `Branch.CreateBranch` refuses any principal that
10
+ * is not an authenticated workspace, and `Branch.MintShare` verifies write
11
+ * access, refuses an expired parent, and caps the child at the parent's
12
+ * expiry. `BranchShare.mint` performs none of those checks, so an in-process
13
+ * host that mints through the service directly must gate the principal and
14
+ * the parent capability itself.
15
+ *
16
+ * @since 0.1.0
17
+ */
18
+ import type * as Layer from "effect/Layer";
19
+ import type * as Rpc from "effect/unstable/rpc/Rpc";
20
+ import type * as RpcGroup from "effect/unstable/rpc/RpcGroup";
21
+ import * as BranchCommands from "./BranchCommands.ts";
22
+ import * as BranchIds from "./BranchIds.ts";
23
+ import * as BranchPresence from "./BranchPresence.ts";
24
+ import { BranchRpcs } from "./BranchRpcs.ts";
25
+ import * as BranchShare from "./BranchShare.ts";
26
+ /**
27
+ * Provides the branch RPC handlers over the branch services.
28
+ *
29
+ * With {@link BranchPresence.layerNoop}, roster reads and watches fail with
30
+ * `unsupported`: unavailable presence cannot establish an empty roster.
31
+ *
32
+ * A roster watch emits the roster as of subscription, then re-lists on every
33
+ * presence change for the branch AND at least once per second, emitting
34
+ * only when the roster it read differs from the one it last sent.
35
+ *
36
+ * All three of those are triggers into ONE sequential reader. The roster is
37
+ * mutable state behind an effect, so concurrent readers can complete out of
38
+ * order, and a watcher that receives an older roster after a newer one keeps
39
+ * the older one: `changesWith` compares against the previous emission, not
40
+ * against the freshest state. Serializing the reads is what makes the last
41
+ * value a watcher holds the most recent one that was read.
42
+ *
43
+ * The periodic re-list is what makes expiry observable. A lapsed lease
44
+ * publishes nothing — the roster drops it the next time somebody lists — so a
45
+ * watch driven by change events alone never saw the LAST participant leave,
46
+ * and a burst of announcements on unrelated branches could slide the one
47
+ * notification this watch needed out of the shared feed. Both are bounded by
48
+ * one second: a departure is visible within 1 s of lease expiry,
49
+ * whether or not a survivor reports it.
50
+ *
51
+ * @category layers
52
+ * @since 0.1.0
53
+ */
54
+ export declare const layerHandlers: Layer.Layer<Rpc.ToHandler<RpcGroup.Rpcs<typeof BranchRpcs>>, never, BranchShare.BranchShare | BranchPresence.BranchPresence | BranchCommands.BranchCommands | BranchIds.BranchIds>;
55
+ //# sourceMappingURL=BranchServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BranchServer.d.ts","sourceRoot":"","sources":["../../src/BranchServer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,KAAK,GAAG,MAAM,yBAAyB,CAAA;AACnD,OAAO,KAAK,KAAK,QAAQ,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAwC/C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,KAAK,CACrC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,UAAU,CAAC,CAAC,EAC/C,KAAK,EACH,WAAW,CAAC,WAAW,GACvB,cAAc,CAAC,cAAc,GAC7B,cAAc,CAAC,cAAc,GAC7B,SAAS,CAAC,SAAS,CAkGtB,CAAA"}
@@ -0,0 +1,147 @@
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 BranchServer_exports = {};
30
+ __export(BranchServer_exports, {
31
+ layerHandlers: () => layerHandlers
32
+ });
33
+ module.exports = __toCommonJS(BranchServer_exports);
34
+ var Clock = __toESM(require("effect/Clock"), 1);
35
+ var Effect = __toESM(require("effect/Effect"), 1);
36
+ var Stream = __toESM(require("effect/Stream"), 1);
37
+ var BranchCommands = __toESM(require("./BranchCommands.js"), 1);
38
+ var BranchIds = __toESM(require("./BranchIds.js"), 1);
39
+ var BranchPresence = __toESM(require("./BranchPresence.js"), 1);
40
+ var import_BranchProtocol = require("./BranchProtocol.js");
41
+ var import_BranchRpcs = require("./BranchRpcs.js");
42
+ var BranchShare = __toESM(require("./BranchShare.js"), 1);
43
+ var Admission = __toESM(require("./internal/Admission.js"), 1);
44
+ var import_SyncError = require("./SyncError.js");
45
+ var SyncPrincipal = __toESM(require("./SyncPrincipal.js"), 1);
46
+ const rosterKey = (participants) => JSON.stringify(
47
+ participants.map((participant) => [
48
+ participant.participantId,
49
+ participant.displayName,
50
+ participant.sessionId,
51
+ participant.kind,
52
+ participant.where,
53
+ participant.watching,
54
+ participant.for_member,
55
+ participant.leaseExpiresAtMs,
56
+ participant.cursor === null ? null : [participant.cursor.cardId, participant.cursor.offset]
57
+ ])
58
+ );
59
+ const sameRoster = (left, right) => rosterKey(left) === rosterKey(right);
60
+ const layerHandlers = import_BranchRpcs.BranchRpcs.toLayer(
61
+ Effect.gen(function* () {
62
+ const share = yield* BranchShare.BranchShare;
63
+ const presence = yield* BranchPresence.BranchPresence;
64
+ const commands = yield* BranchCommands.BranchCommands;
65
+ const ids = yield* BranchIds.BranchIds;
66
+ return import_BranchRpcs.BranchRpcs.of({
67
+ "Branch.CreateBranch": ({ ttlMs }) => Effect.gen(function* () {
68
+ const principal = yield* SyncPrincipal.SyncPrincipal;
69
+ if (!SyncPrincipal.isWorkspace(principal)) {
70
+ return yield* new import_SyncError.SyncError({
71
+ code: "unauthorized",
72
+ message: "Creating a branch requires an authenticated workspace principal"
73
+ });
74
+ }
75
+ const branchId = yield* Admission.decode(import_BranchProtocol.BranchId, yield* ids.fresh, "invalid_request");
76
+ const capability = yield* share.mint({
77
+ branchId,
78
+ capabilityId: yield* ids.fresh,
79
+ access: "write",
80
+ ttlMs
81
+ });
82
+ return { branchId: capability.claims.branchId, capability };
83
+ }),
84
+ "Branch.MintShare": ({ capability, access, ttlMs }) => Effect.gen(function* () {
85
+ const claims = yield* share.verify(capability, {
86
+ branchId: capability.claims.branchId,
87
+ access: "write"
88
+ });
89
+ const capabilityId = yield* ids.fresh;
90
+ const nowMs = yield* Clock.currentTimeMillis;
91
+ if (nowMs >= claims.expiresAtMs) {
92
+ return yield* new import_SyncError.SyncError({
93
+ code: "unauthorized",
94
+ message: "The share capability has expired"
95
+ });
96
+ }
97
+ return yield* share.mint({
98
+ branchId: claims.branchId,
99
+ capabilityId,
100
+ access,
101
+ ttlMs,
102
+ maxExpiresAtMs: claims.expiresAtMs
103
+ });
104
+ }),
105
+ "Branch.Submit": (payload) => commands.submit(payload),
106
+ "Branch.Announce": (payload) => presence.announce(payload),
107
+ "Branch.Leave": (payload) => Effect.as(presence.leave(payload), null),
108
+ "Branch.Roster": (payload) => presence.list(payload),
109
+ "Branch.PresenceOn": (payload) => presence.presenceOn(payload),
110
+ "Branch.WatchRoster": (payload) => Stream.merge(
111
+ // The subscription's own snapshot is a TRIGGER like the other two,
112
+ // not a fourth reader. Reading it here concurrently with them let
113
+ // the roster a change produced overtake it: `presence.list` is an
114
+ // effect against mutable state, three of them ran at once, and the
115
+ // slowest one won the merge. `changesWith` compares an emission only
116
+ // against the one before it, so the older roster passed the filter
117
+ // and the watcher's LAST value said a participant who had already
118
+ // left was still present, until the next change or lease tick.
119
+ Stream.succeed(void 0),
120
+ Stream.merge(
121
+ presence.changes.pipe(
122
+ Stream.filter((branchId) => branchId === payload.branchId),
123
+ Stream.map(() => void 0)
124
+ ),
125
+ // A lapsed lease publishes nothing, so a watch driven only by
126
+ // change events never observes the LAST participant leaving, and
127
+ // an unrelated branch's burst can slide the one notification this
128
+ // watch needed out of the shared feed. Re-listing at least once per second
129
+ // bounds both: a departure is visible within 1 s of expiry, whether or not anyone reports it.
130
+ Stream.fromEffectRepeat(Effect.sleep(Math.min(presence.leaseMs, 1e3))).pipe(Stream.map(() => void 0))
131
+ )
132
+ ).pipe(
133
+ // ONE reader, sequential by default, so a roster is read in the
134
+ // order its trigger arrived and every emission is at least as fresh
135
+ // as the one before it.
136
+ Stream.mapEffect(() => presence.list(payload)),
137
+ // Deduplicated over the WHOLE stream, initial emission included.
138
+ // Applied to the change half alone, its first value always passed,
139
+ // so a change that left the roster identical still sent the watcher
140
+ // a second copy of what it had just been given.
141
+ Stream.changesWith(sameRoster),
142
+ Stream.map((participants) => ({ participants }))
143
+ )
144
+ });
145
+ })
146
+ );
147
+ //# sourceMappingURL=BranchServer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/BranchServer.ts"],
4
+ "sourcesContent": ["/**\n * The server-side handlers of {@link BranchRpcs}: a thin projection of the\n * branch services onto the wire.\n *\n * Five procedures forward to the service that owns their boundary and add\n * nothing: `Branch.Submit` to {@link BranchCommands}, and `Branch.Announce`,\n * `Branch.Leave`, `Branch.Roster`, and `Branch.WatchRoster` to\n * {@link BranchPresence}. Two enforce policy here, on top of what\n * {@link BranchShare} checks: `Branch.CreateBranch` refuses any principal that\n * is not an authenticated workspace, and `Branch.MintShare` verifies write\n * access, refuses an expired parent, and caps the child at the parent's\n * expiry. `BranchShare.mint` performs none of those checks, so an in-process\n * host that mints through the service directly must gate the principal and\n * the parent capability itself.\n *\n * @since 0.1.0\n */\n\nimport * as Clock from \"effect/Clock\"\nimport * as Effect from \"effect/Effect\"\nimport type * as Layer from \"effect/Layer\"\nimport * as Stream from \"effect/Stream\"\nimport type * as Rpc from \"effect/unstable/rpc/Rpc\"\nimport type * as RpcGroup from \"effect/unstable/rpc/RpcGroup\"\nimport * as BranchCommands from \"./BranchCommands.ts\"\nimport * as BranchIds from \"./BranchIds.ts\"\nimport * as BranchPresence from \"./BranchPresence.ts\"\nimport { BranchId, type Participant } from \"./BranchProtocol.ts\"\nimport { BranchRpcs } from \"./BranchRpcs.ts\"\nimport * as BranchShare from \"./BranchShare.ts\"\nimport * as Admission from \"./internal/Admission.ts\"\nimport { SyncError } from \"./SyncError.ts\"\nimport * as SyncPrincipal from \"./SyncPrincipal.ts\"\n\n/**\n * Whether two roster emissions say the same thing.\n *\n * `list` returns participants sorted by id, so the comparison is positional.\n * The lease itself is part of the key: a heartbeat that only extends a lease\n * IS a change the watcher wants, because it is the evidence the participant is\n * still there.\n *\n * The rendering is JSON rather than delimiter-joined fields. Participant ids\n * and display names are `NonEmptyString` with no charset constraint, so every\n * separator is a character they may carry, including the control bytes this\n * used to join on. Two rosters differing only in where a field boundary falls\n * then rendered identically and a watcher never observed the change. JSON\n * quotes and escapes each field, so distinct rosters render distinctly.\n */\nconst rosterKey = (participants: ReadonlyArray<Participant>): string =>\n JSON.stringify(\n participants.map((participant) => [\n participant.participantId,\n participant.displayName,\n participant.sessionId,\n participant.kind,\n participant.where,\n participant.watching,\n participant.for_member,\n participant.leaseExpiresAtMs,\n participant.cursor === null ? null : [participant.cursor.cardId, participant.cursor.offset]\n ])\n )\n\nconst sameRoster = (\n left: ReadonlyArray<Participant>,\n right: ReadonlyArray<Participant>\n): boolean => rosterKey(left) === rosterKey(right)\n\n/**\n * Provides the branch RPC handlers over the branch services.\n *\n * With {@link BranchPresence.layerNoop}, roster reads and watches fail with\n * `unsupported`: unavailable presence cannot establish an empty roster.\n *\n * A roster watch emits the roster as of subscription, then re-lists on every\n * presence change for the branch AND at least once per second, emitting\n * only when the roster it read differs from the one it last sent.\n *\n * All three of those are triggers into ONE sequential reader. The roster is\n * mutable state behind an effect, so concurrent readers can complete out of\n * order, and a watcher that receives an older roster after a newer one keeps\n * the older one: `changesWith` compares against the previous emission, not\n * against the freshest state. Serializing the reads is what makes the last\n * value a watcher holds the most recent one that was read.\n *\n * The periodic re-list is what makes expiry observable. A lapsed lease\n * publishes nothing \u2014 the roster drops it the next time somebody lists \u2014 so a\n * watch driven by change events alone never saw the LAST participant leave,\n * and a burst of announcements on unrelated branches could slide the one\n * notification this watch needed out of the shared feed. Both are bounded by\n * one second: a departure is visible within 1 s of lease expiry,\n * whether or not a survivor reports it.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerHandlers: Layer.Layer<\n Rpc.ToHandler<RpcGroup.Rpcs<typeof BranchRpcs>>,\n never,\n | BranchShare.BranchShare\n | BranchPresence.BranchPresence\n | BranchCommands.BranchCommands\n | BranchIds.BranchIds\n> = BranchRpcs.toLayer(\n Effect.gen(function*() {\n const share = yield* BranchShare.BranchShare\n const presence = yield* BranchPresence.BranchPresence\n const commands = yield* BranchCommands.BranchCommands\n const ids = yield* BranchIds.BranchIds\n\n return BranchRpcs.of({\n \"Branch.CreateBranch\": ({ ttlMs }) =>\n Effect.gen(function*() {\n const principal = yield* SyncPrincipal.SyncPrincipal\n if (!SyncPrincipal.isWorkspace(principal)) {\n return yield* new SyncError({\n code: \"unauthorized\",\n message: \"Creating a branch requires an authenticated workspace principal\"\n })\n }\n // Decoded, not branded: `BranchIds` is a host port whose `fresh` is\n // typed `Effect<string>`, so a host id source yielding \"\" turned an\n // asserted `BranchId` into a schema defect inside `mint` rather than\n // the `SyncError` this handler declares.\n const branchId = yield* Admission.decode(BranchId, yield* ids.fresh, \"invalid_request\")\n const capability = yield* share.mint({\n branchId,\n capabilityId: yield* ids.fresh,\n access: \"write\",\n ttlMs\n })\n return { branchId: capability.claims.branchId, capability }\n }),\n \"Branch.MintShare\": ({ capability, access, ttlMs }) =>\n Effect.gen(function*() {\n const claims = yield* share.verify(capability, {\n branchId: capability.claims.branchId,\n access: \"write\"\n })\n const capabilityId = yield* ids.fresh\n const nowMs = yield* Clock.currentTimeMillis\n // ID generation may outlast the parent. Refuse before minting, and\n // pass the absolute ceiling so the signing clock cannot extend it.\n if (nowMs >= claims.expiresAtMs) {\n return yield* new SyncError({\n code: \"unauthorized\",\n message: \"The share capability has expired\"\n })\n }\n // A link never outlives the capability it was minted from.\n return yield* share.mint({\n branchId: claims.branchId,\n capabilityId,\n access,\n ttlMs,\n maxExpiresAtMs: claims.expiresAtMs\n })\n }),\n \"Branch.Submit\": (payload) => commands.submit(payload),\n \"Branch.Announce\": (payload) => presence.announce(payload),\n \"Branch.Leave\": (payload) => Effect.as(presence.leave(payload), null),\n \"Branch.Roster\": (payload) => presence.list(payload),\n \"Branch.PresenceOn\": (payload) => presence.presenceOn(payload),\n \"Branch.WatchRoster\": (payload) =>\n Stream.merge(\n // The subscription's own snapshot is a TRIGGER like the other two,\n // not a fourth reader. Reading it here concurrently with them let\n // the roster a change produced overtake it: `presence.list` is an\n // effect against mutable state, three of them ran at once, and the\n // slowest one won the merge. `changesWith` compares an emission only\n // against the one before it, so the older roster passed the filter\n // and the watcher's LAST value said a participant who had already\n // left was still present, until the next change or lease tick.\n Stream.succeed(undefined),\n Stream.merge(\n presence.changes.pipe(\n Stream.filter((branchId) => branchId === payload.branchId),\n Stream.map(() => undefined)\n ),\n // A lapsed lease publishes nothing, so a watch driven only by\n // change events never observes the LAST participant leaving, and\n // an unrelated branch's burst can slide the one notification this\n // watch needed out of the shared feed. Re-listing at least once per second\n // bounds both: a departure is visible within 1 s of expiry, whether or not anyone reports it.\n Stream.fromEffectRepeat(Effect.sleep(Math.min(presence.leaseMs, 1_000))).pipe(Stream.map(() => undefined))\n )\n ).pipe(\n // ONE reader, sequential by default, so a roster is read in the\n // order its trigger arrived and every emission is at least as fresh\n // as the one before it.\n Stream.mapEffect(() => presence.list(payload)),\n // Deduplicated over the WHOLE stream, initial emission included.\n // Applied to the change half alone, its first value always passed,\n // so a change that left the roster identical still sent the watcher\n // a second copy of what it had just been given.\n Stream.changesWith(sameRoster),\n Stream.map((participants) => ({ participants }))\n )\n })\n })\n)\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }