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

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 @@
1
+ {"version":3,"file":"BranchCommands.d.ts","sourceRoot":"","sources":["../../src/BranchCommands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEzC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAE1C,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,SAAS,EACd,cAAc,EAGd,iBAAiB,EACjB,aAAa,EACd,MAAM,qBAAqB,CAAA;AAE5B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAK/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG1C;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,SAAS,CAAC,CAAA;CACtF;;AAED;;;;;GAKG;AACH,qBAAa,cAAe,SAAQ,mBAAyE;CAAG;AAEhH;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,YAAW,OAAO,CAAC,OAAO,CAAM,KAAG,OAIxD,CAAA;AAEJ;;;;;GAKG;AACH,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAA6C,CAAA;AAuB/F;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAA;AAEzC;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,QAAc,CAAA;AAEjD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,QAAqB,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,qBAAqB,OAAwB,CAAA;AAE1D;;;;;;;;;GASG;AACH,MAAM,WAAW,OAAO;IACtB;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3C;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7C;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5C;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC3C;AAgWD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GACvB,UAAS,OAAY,KACpB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,GAAG,WAAW,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CA8BzF,CAAA;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,GAAG,WAAW,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CACxF,CAAA;AAEpB;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,GAAG,WAAW,CAAC,WAAW,CAG/F,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACpB,SAAS,OAAO,KACf,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,SAAS,EAAE,OAAO,CAAC,OAAO,GAAG,WAAW,CAAC,WAAW,CAC9B,CAAA;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ;IACjC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC,eAAe,CAAC,CAAA;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB,KAAG,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,SAAS,CAYvB,CAAA"}
@@ -0,0 +1,316 @@
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 BranchCommands_exports = {};
30
+ __export(BranchCommands_exports, {
31
+ BranchCommands: () => BranchCommands,
32
+ defaultBranchIdleMs: () => defaultBranchIdleMs,
33
+ defaultHydrationLimit: () => defaultHydrationLimit,
34
+ defaultLedgerCapacity: () => defaultLedgerCapacity,
35
+ defaultMaxCommandBytes: () => defaultMaxCommandBytes,
36
+ layer: () => layer,
37
+ layerNoop: () => layerNoop,
38
+ layerWith: () => layerWith,
39
+ makeLive: () => makeLive,
40
+ makeLiveWith: () => makeLiveWith,
41
+ makeNoop: () => makeNoop,
42
+ submission: () => submission
43
+ });
44
+ module.exports = __toCommonJS(BranchCommands_exports);
45
+ var import_journal = require("@smthrs/journal");
46
+ var JournalEvent = __toESM(require("@smthrs/journal/JournalEvent"), 1);
47
+ var Context = __toESM(require("effect/Context"), 1);
48
+ var Effect = __toESM(require("effect/Effect"), 1);
49
+ var Layer = __toESM(require("effect/Layer"), 1);
50
+ var RcMap = __toESM(require("effect/RcMap"), 1);
51
+ var Semaphore = __toESM(require("effect/Semaphore"), 1);
52
+ var import_BranchProtocol = require("./BranchProtocol.js");
53
+ var import_BranchRpcs = require("./BranchRpcs.js");
54
+ var BranchShare = __toESM(require("./BranchShare.js"), 1);
55
+ var Admission = __toESM(require("./internal/Admission.js"), 1);
56
+ var import_CauseText = require("./internal/CauseText.js");
57
+ var import_JournalErrorCode = require("./internal/JournalErrorCode.js");
58
+ var import_PolicyOptions = require("./internal/PolicyOptions.js");
59
+ var import_SyncError = require("./SyncError.js");
60
+ var SyncProtocol = __toESM(require("./SyncProtocol.js"), 1);
61
+ class BranchCommands extends Context.Service()("@smthrs/sync/BranchCommands") {
62
+ }
63
+ const makeNoop = (overrides = {}) => BranchCommands.of({
64
+ submit: () => Effect.fail(new import_SyncError.SyncError({ code: "closed", message: "Branch commands are unavailable" })),
65
+ ...overrides
66
+ });
67
+ const layerNoop = Layer.succeed(BranchCommands, makeNoop());
68
+ const journalFailure = (cause) => new import_SyncError.SyncError({
69
+ code: (0, import_JournalErrorCode.journalErrorCode)(cause),
70
+ message: "Branch journal write failed",
71
+ cause: (0, import_CauseText.causeCode)(cause)
72
+ });
73
+ const pageSize = 256;
74
+ const defaultLedgerCapacity = 4096;
75
+ const defaultMaxCommandBytes = 1024 * 1024;
76
+ const defaultBranchIdleMs = import_BranchRpcs.maximumBranchTtlMs;
77
+ const defaultHydrationLimit = defaultLedgerCapacity;
78
+ const makeWith = (resolved) => Effect.gen(
79
+ function* () {
80
+ const journal = yield* import_journal.Journal.Journal;
81
+ const share = yield* BranchShare.BranchShare;
82
+ const { branchIdleMs, hydrationLimit, ledgerCapacity, maxCommandBytes, maxFrameBytes } = resolved;
83
+ const branches = yield* RcMap.make({
84
+ idleTimeToLive: branchIdleMs,
85
+ lookup: (_branchId) => Effect.sync(() => ({
86
+ permit: Semaphore.makeUnsafe(1),
87
+ receipts: /* @__PURE__ */ new Map(),
88
+ cursor: void 0,
89
+ hydrated: false
90
+ }))
91
+ });
92
+ const recordIn = (branch, receipt) => {
93
+ branch.delete(receipt.commandId);
94
+ branch.set(receipt.commandId, receipt);
95
+ for (const oldest of branch.keys()) {
96
+ if (branch.size <= ledgerCapacity) break;
97
+ branch.delete(oldest);
98
+ }
99
+ };
100
+ const duplicateOf = (known) => new import_BranchProtocol.CommandReceipt({
101
+ branchId: known.branchId,
102
+ commandId: known.commandId,
103
+ status: "duplicate",
104
+ seq: known.seq
105
+ });
106
+ const walk = (branchId, from, budget, visit) => Effect.gen(function* () {
107
+ const runId = (0, import_BranchProtocol.branchRunId)(branchId);
108
+ let after = from;
109
+ let hasMore = true;
110
+ let remaining = budget;
111
+ while (hasMore && remaining > 0) {
112
+ const page = yield* journal.entries({
113
+ runId,
114
+ ...after === void 0 ? {} : { after },
115
+ limit: Math.min(pageSize, remaining)
116
+ }).pipe(Effect.mapError(journalFailure));
117
+ const admitted = yield* Admission.withCommands(page.entries, runId, after ?? -1);
118
+ for (const { command, entry } of admitted) {
119
+ after = entry.seq;
120
+ remaining -= 1;
121
+ visit(entry, command?.commandId);
122
+ }
123
+ if (page.entries.length === 0) return;
124
+ hasMore = page.hasMore;
125
+ }
126
+ });
127
+ const replay = (branchId, state, budget) => Effect.gen(function* () {
128
+ const staged = new Map(state.receipts);
129
+ let position = state.cursor;
130
+ yield* walk(branchId, position, budget, (entry, commandId) => {
131
+ position = entry.seq;
132
+ if (commandId !== void 0) {
133
+ recordIn(staged, new import_BranchProtocol.CommandReceipt({ branchId, commandId, status: "admitted", seq: entry.seq }));
134
+ }
135
+ });
136
+ state.cursor = position;
137
+ state.receipts = staged;
138
+ });
139
+ const admittedSeq = (branchId, commandId) => Effect.gen(function* () {
140
+ let found;
141
+ yield* walk(branchId, void 0, Number.POSITIVE_INFINITY, (entry, admitted) => {
142
+ if (admitted === commandId) found = entry.seq;
143
+ });
144
+ return found;
145
+ });
146
+ const hydrate = (branchId, state) => Effect.gen(function* () {
147
+ if (state.hydrated) return;
148
+ yield* replay(branchId, state, hydrationLimit);
149
+ state.hydrated = true;
150
+ });
151
+ const lostRace = (submission2, state, cause) => Effect.gen(function* () {
152
+ yield* replay(submission2.branchId, state, Number.POSITIVE_INFINITY);
153
+ const known = state.receipts.get(submission2.commandId);
154
+ if (known !== void 0) return duplicateOf(known);
155
+ const mayHaveEvicted = state.receipts.size >= ledgerCapacity;
156
+ const seq = mayHaveEvicted ? yield* admittedSeq(submission2.branchId, submission2.commandId) : void 0;
157
+ if (seq === void 0) return yield* Effect.fail(journalFailure(cause));
158
+ return new import_BranchProtocol.CommandReceipt({
159
+ branchId: submission2.branchId,
160
+ commandId: submission2.commandId,
161
+ status: "duplicate",
162
+ seq
163
+ });
164
+ });
165
+ const journalInput = (submission2, capabilityId) => new JournalEvent.Input({
166
+ runId: (0, import_BranchProtocol.branchRunId)(submission2.branchId),
167
+ sourceId: (0, import_BranchProtocol.commandSourceId)(submission2.commandId),
168
+ sourceSeq: import_BranchProtocol.commandSourceSeq,
169
+ eventType: import_BranchProtocol.CommandEvent,
170
+ payload: {
171
+ branchId: submission2.branchId,
172
+ commandId: submission2.commandId,
173
+ participantId: submission2.participantId,
174
+ name: submission2.name,
175
+ args: submission2.args,
176
+ target: submission2.target,
177
+ capabilityId
178
+ },
179
+ meta: null
180
+ });
181
+ const admit = (request, capabilityId, state) => Effect.gen(function* () {
182
+ const submission2 = request.submission;
183
+ yield* hydrate(submission2.branchId, state);
184
+ const known = state.receipts.get(submission2.commandId);
185
+ if (known !== void 0) return duplicateOf(known);
186
+ const receipt = yield* journal.emitDurableUnfenced(
187
+ journalInput(submission2, capabilityId)
188
+ ).pipe(
189
+ // A `Duplicate` receipt is another writer landing the identical
190
+ // submission first: the journal deduplicated durably and returned
191
+ // the canonical sequence the original append committed at.
192
+ Effect.map(
193
+ (accepted) => new import_BranchProtocol.CommandReceipt({
194
+ branchId: submission2.branchId,
195
+ commandId: submission2.commandId,
196
+ status: accepted._tag === "Duplicate" ? "duplicate" : "admitted",
197
+ seq: accepted.seq
198
+ })
199
+ ),
200
+ Effect.catch(
201
+ (cause) => cause.code === "idempotency_conflict" ? lostRace(submission2, state, cause) : Effect.fail(journalFailure(cause))
202
+ )
203
+ );
204
+ recordIn(
205
+ state.receipts,
206
+ new import_BranchProtocol.CommandReceipt({
207
+ branchId: submission2.branchId,
208
+ commandId: submission2.commandId,
209
+ status: "admitted",
210
+ seq: receipt.seq
211
+ })
212
+ );
213
+ return receipt;
214
+ });
215
+ const submit = Effect.fn("BranchCommands.submit")(function* (supplied) {
216
+ const decoded = yield* Admission.decode(import_BranchProtocol.SubmitRequest, supplied, "invalid_request");
217
+ const request = { capability: decoded.capability, submission: { ...decoded.submission } };
218
+ yield* Effect.annotateCurrentSpan({
219
+ branchId: request.submission.branchId,
220
+ commandId: request.submission.commandId,
221
+ participantId: request.submission.participantId
222
+ });
223
+ const claims = yield* share.verify(request.capability, {
224
+ branchId: request.submission.branchId,
225
+ access: "write"
226
+ });
227
+ const bytes = SyncProtocol.encodedByteLength(request.submission);
228
+ if (bytes > maxCommandBytes) {
229
+ return yield* Effect.fail(
230
+ new import_SyncError.SyncError({
231
+ code: "frame_too_large",
232
+ message: `Encoded command submission of ${bytes} bytes exceeds the ${maxCommandBytes}-byte ceiling`
233
+ })
234
+ );
235
+ }
236
+ const input = journalInput(request.submission, claims.capabilityId);
237
+ const entryBytes = SyncProtocol.encodedByteLength({
238
+ ...input,
239
+ seq: Number.MAX_SAFE_INTEGER - 1,
240
+ eventId: JournalEvent.makeEventId(input.runId, input.sourceId, import_BranchProtocol.commandSourceSeq),
241
+ emittedAtMs: Number.MAX_VALUE
242
+ });
243
+ if (entryBytes > maxFrameBytes) {
244
+ return yield* Effect.fail(
245
+ new import_SyncError.SyncError({
246
+ code: "frame_too_large",
247
+ message: `Encoded command journal entry requires up to ${entryBytes} bytes, exceeding the ${maxFrameBytes}-byte frame ceiling`
248
+ })
249
+ );
250
+ }
251
+ return yield* RcMap.get(branches, request.submission.branchId).pipe(
252
+ Effect.flatMap((state) => state.permit.withPermit(admit(request, claims.capabilityId, state))),
253
+ Effect.scoped
254
+ );
255
+ });
256
+ return BranchCommands.of({ submit });
257
+ }
258
+ );
259
+ const defaults = {
260
+ maxCommandBytes: defaultMaxCommandBytes,
261
+ maxFrameBytes: SyncProtocol.defaultMaxFrameBytes,
262
+ ledgerCapacity: defaultLedgerCapacity,
263
+ hydrationLimit: defaultHydrationLimit,
264
+ branchIdleMs: defaultBranchIdleMs
265
+ };
266
+ const makeLiveWith = (options = {}) => Effect.flatMap(
267
+ Effect.all({
268
+ maxFrameBytes: (0, import_PolicyOptions.positiveInt)(
269
+ "BranchCommands.Options.maxFrameBytes",
270
+ options.maxFrameBytes,
271
+ defaults.maxFrameBytes
272
+ ),
273
+ maxCommandBytes: (0, import_PolicyOptions.positiveInt)(
274
+ "BranchCommands.Options.maxCommandBytes",
275
+ options.maxCommandBytes,
276
+ defaults.maxCommandBytes
277
+ ),
278
+ ledgerCapacity: (0, import_PolicyOptions.positiveInt)(
279
+ "BranchCommands.Options.ledgerCapacity",
280
+ options.ledgerCapacity,
281
+ defaults.ledgerCapacity
282
+ ),
283
+ hydrationLimit: (0, import_PolicyOptions.positiveInt)(
284
+ "BranchCommands.Options.hydrationLimit",
285
+ options.hydrationLimit,
286
+ defaults.hydrationLimit
287
+ ),
288
+ branchIdleMs: (0, import_PolicyOptions.positiveInt)(
289
+ "BranchCommands.Options.branchIdleMs",
290
+ options.branchIdleMs,
291
+ defaults.branchIdleMs
292
+ )
293
+ }),
294
+ makeWith
295
+ );
296
+ const makeLive = makeWith(defaults);
297
+ const layer = Layer.effect(
298
+ BranchCommands,
299
+ makeLive
300
+ );
301
+ const layerWith = (options) => Layer.effect(BranchCommands, makeLiveWith(options));
302
+ const submission = (fields) => (
303
+ // Decoded, not constructed: `name` is a `NonEmptyString` the parameter type
304
+ // admits `""` for, and `new CommandSubmission` throws on it. A builder that
305
+ // dies on a caller's argument is the same defect `submit` already refuses
306
+ // typed, one call earlier.
307
+ Admission.decode(import_BranchProtocol.CommandSubmission, {
308
+ branchId: fields.branchId,
309
+ commandId: fields.commandId,
310
+ participantId: fields.participantId,
311
+ name: fields.name,
312
+ args: fields.args ?? "",
313
+ target: fields.target ?? ""
314
+ }, "invalid_request")
315
+ );
316
+ //# sourceMappingURL=BranchCommands.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/BranchCommands.ts"],
4
+ "sourcesContent": ["/**\n * Idempotent admission of collaborative commands onto a branch document.\n *\n * Three different things look identical from the server's side \u2014 an optimistic\n * client re-sending after a timeout, a reconnecting client flushing its\n * outbox, and two people pressing the same button at once \u2014 and all three must\n * produce exactly one durable command. The client-minted `commandId` is the\n * idempotency key for all three.\n *\n * The exactly-once constraint is durable, not process-local: every append\n * carries the producer identity `(branch run, commandSourceId(commandId),\n * commandSourceSeq)`, which the journal enforces inside its own write\n * transaction. Two independently constructed servers that race the same\n * command therefore collide in the journal \u2014 one appends, the other receives\n * a duplicate receipt or an idempotency conflict and resolves the canonical\n * sequence by replaying the branch (audit finding F-14). The in-memory\n * ledger, permit, and replay cursor are a fast path only: they answer known\n * duplicates without a journal write and keep a restarted server from\n * re-executing history, but correctness never depends on them.\n *\n * @since 0.1.0\n */\n\nimport { Journal } from \"@smthrs/journal\"\nimport * as JournalEvent from \"@smthrs/journal/JournalEvent\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as RcMap from \"effect/RcMap\"\nimport type * as Scope from \"effect/Scope\"\nimport * as Semaphore from \"effect/Semaphore\"\nimport {\n type BranchId,\n branchRunId,\n CommandEvent,\n type CommandId,\n CommandReceipt,\n commandSourceId,\n commandSourceSeq,\n CommandSubmission,\n SubmitRequest\n} from \"./BranchProtocol.ts\"\nimport { maximumBranchTtlMs } from \"./BranchRpcs.ts\"\nimport * as BranchShare from \"./BranchShare.ts\"\nimport * as Admission from \"./internal/Admission.ts\"\nimport { causeCode } from \"./internal/CauseText.ts\"\nimport { journalErrorCode } from \"./internal/JournalErrorCode.ts\"\nimport { positiveInt } from \"./internal/PolicyOptions.ts\"\nimport { SyncError } from \"./SyncError.ts\"\nimport * as SyncProtocol from \"./SyncProtocol.ts\"\n\n/**\n * Branch command admission operations.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n readonly submit: (request: SubmitRequest) => Effect.Effect<CommandReceipt, SyncError>\n}\n\n/**\n * The branch command ledger.\n *\n * @category services\n * @since 0.1.0\n */\nexport class BranchCommands extends Context.Service<BranchCommands, Service>()(\"@smthrs/sync/BranchCommands\") {}\n\n/**\n * Constructs a command ledger that admits nothing.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service =>\n BranchCommands.of({\n submit: () => Effect.fail(new SyncError({ code: \"closed\", message: \"Branch commands are unavailable\" })),\n ...overrides\n })\n\n/**\n * Provides a command ledger that admits nothing.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop: Layer.Layer<BranchCommands> = Layer.succeed(BranchCommands, makeNoop())\n\n/**\n * Projects a journal write failure onto the sync boundary.\n *\n * The public `message` is a constant and the `cause` names the failure's type\n * only. A branch writer may hold nothing but a share link, and the journal's\n * own message is the SQLite driver's, which carries SQL text, table and column\n * names, and constraint identifiers. The CODE is a different question from the\n * message: a journal code this boundary also declares crosses as that code, so\n * a writer whose commit was lost to a closing journal is told that rather than\n * being told nothing.\n */\nconst journalFailure = (cause: unknown): SyncError =>\n new SyncError({\n code: journalErrorCode(cause),\n message: \"Branch journal write failed\",\n cause: causeCode(cause)\n })\n\n/** How many entries one rehydration page reads. */\nconst pageSize = 256\n\n/**\n * Receipts one branch keeps in memory before the oldest are evicted.\n *\n * The ledger is a fast path: it answers a known duplicate without a journal\n * write, and losing an entry costs a round trip, never correctness, because\n * the journal's own producer identity is the durable exactly-once constraint\n * and returns a `Duplicate` receipt for a command already admitted. Retaining\n * one receipt per command forever meant a process serving a hundred branches\n * of a hundred thousand commands held ten million of them, so the fast path\n * is bounded and the durable path is what makes it safe to bound.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultLedgerCapacity = 4096\n\n/**\n * Inclusive UTF-8 JSON submission ceiling. The separate sync entry ceiling\n * leaves room for the durable envelope around every admitted command.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultMaxCommandBytes = 1024 * 1024\n\n/**\n * Milliseconds a branch may have no active submissions before its in-memory state is\n * dropped.\n *\n * The receipt ledger is bounded per branch; this bounds the branches. A\n * dropped branch re-hydrates on its next submission exactly as it does after\n * a restart, so eviction costs a journal read, never correctness. The default\n * is the longest lifetime a branch capability may carry, so a branch is only\n * dropped once every capability minted at its last touch can have expired.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultBranchIdleMs = maximumBranchTtlMs\n\n/**\n * Entries one branch's first-touch hydration reads before it stops.\n *\n * Hydration is the only full history read that ever sat on the WRITE path: the\n * first submission a process makes to a branch paged that branch's whole\n * durable log, 256 entries at a time, while holding the branch's admission\n * permit, before its own append was attempted. A hundred-thousand-entry branch\n * therefore charged its next writer four hundred journal reads for a fast path\n * that keeps four thousand receipts.\n *\n * The budget defaults to {@link defaultLedgerCapacity} because hydration\n * cannot seed more receipts than the ledger holds: everything a longer walk\n * records displaces something the same walk already recorded. What the extra\n * reads buy is a FRESHER selection \u2014 the tail of history rather than its head\n * \u2014 and the price is an unbounded wait on somebody's write. This trades that\n * back: the walk stops, the cursor stays where it stopped, and a later replay\n * continues forward from there.\n *\n * A branch longer than the budget starts with a partly cold fast path and\n * nothing else. Correctness never rested on the ledger: the journal's producer\n * identity is the durable exactly-once constraint, an exact retry receives a\n * `Duplicate` receipt carrying the canonical sequence, and an\n * `idempotency_conflict` is resolved by a read of the whole history that the\n * ledger's own bound already made necessary.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultHydrationLimit = defaultLedgerCapacity\n\n/**\n * Admission policy.\n *\n * The default caps one encoded command submission at 1 MiB, one branch's\n * in-memory receipt ledger at {@link defaultLedgerCapacity} commands, and one\n * branch's first-touch hydration at {@link defaultHydrationLimit} entries.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Options {\n /**\n * Largest encoded journal entry followers accept. Defaults to\n * {@link SyncProtocol.defaultMaxFrameBytes}. Configure the server and client\n * with the same or a larger ceiling. Admission reserves the full durable\n * envelope, including generated sequence and timestamp fields, before writing.\n */\n readonly maxFrameBytes?: number | undefined\n /**\n * Largest encoded command submission admitted, in bytes. Defaults to\n * {@link defaultMaxCommandBytes}.\n */\n readonly maxCommandBytes?: number | undefined\n /**\n * Receipts one branch keeps in memory before the oldest are evicted.\n * Defaults to {@link defaultLedgerCapacity}.\n */\n readonly ledgerCapacity?: number | undefined\n /**\n * Entries one branch's first-touch hydration reads before it stops.\n * Defaults to {@link defaultHydrationLimit}.\n */\n readonly hydrationLimit?: number | undefined\n /**\n * Milliseconds a branch may have no active submissions before its in-memory state is\n * dropped. Defaults to {@link defaultBranchIdleMs}.\n */\n readonly branchIdleMs?: number | undefined\n}\n\n/** Everything one process holds for one branch. */\ninterface BranchState {\n // One permit PER BRANCH. A single process-wide permit serialized admission\n // across every branch the process served, so one branch's first-touch\n // history replay blocked every other branch's writes.\n readonly permit: Semaphore.Semaphore\n receipts: Map<CommandId, CommandReceipt>\n cursor: JournalEvent.Seq | undefined\n hydrated: boolean\n}\n\n/** The ledger over an already-validated policy. */\nconst makeWith = (\n resolved: {\n readonly maxCommandBytes: number\n readonly maxFrameBytes: number\n readonly ledgerCapacity: number\n readonly hydrationLimit: number\n readonly branchIdleMs: number\n }\n): Effect.Effect<Service, never, Journal.Journal | BranchShare.BranchShare | Scope.Scope> =>\n Effect.gen(\n function*() {\n const journal = yield* Journal.Journal\n const share = yield* BranchShare.BranchShare\n const { branchIdleMs, hydrationLimit, ledgerCapacity, maxCommandBytes, maxFrameBytes } = resolved\n // Active submissions share one state; its idle lifetime starts at release.\n const branches = yield* RcMap.make({\n idleTimeToLive: branchIdleMs,\n lookup: (_branchId: BranchId) =>\n Effect.sync((): BranchState => ({\n permit: Semaphore.makeUnsafe(1),\n receipts: new Map(),\n cursor: undefined,\n hydrated: false\n }))\n })\n\n /**\n * Records one receipt, evicting the branch's oldest once the branch is\n * at capacity. `Map` iterates in insertion order, so the first key is\n * the least recently recorded one; re-recording a known command\n * refreshes it in place rather than growing the branch.\n */\n const recordIn = (branch: Map<CommandId, CommandReceipt>, receipt: CommandReceipt): void => {\n branch.delete(receipt.commandId)\n branch.set(receipt.commandId, receipt)\n for (const oldest of branch.keys()) {\n if (branch.size <= ledgerCapacity) break\n branch.delete(oldest)\n }\n }\n\n const duplicateOf = (known: CommandReceipt): CommandReceipt =>\n new CommandReceipt({\n branchId: known.branchId,\n commandId: known.commandId,\n status: \"duplicate\",\n seq: known.seq\n })\n\n /**\n * Pages a branch's journal forward from `from`, handing each entry to\n * `visit` along with the command identity it carries, if any, and\n * stopping once it has visited `budget` entries. Admission already\n * decoded every command payload in full, so the identity is read off\n * that decoded command rather than decoded a second time.\n *\n * The budget is entries VISITED, not pages read, so a caller names a\n * bound in the unit its own memory is measured in.\n * `Number.POSITIVE_INFINITY` is the unbounded walk the recovery reads\n * take: they answer a question about the whole durable history, and a\n * bounded answer to that question is a wrong one.\n */\n const walk = (\n branchId: BranchId,\n from: JournalEvent.Seq | undefined,\n budget: number,\n visit: (entry: JournalEvent.Entry, commandId: CommandId | undefined) => void\n ): Effect.Effect<void, SyncError> =>\n Effect.gen(function*() {\n const runId = branchRunId(branchId)\n let after = from\n let hasMore = true\n let remaining = budget\n while (hasMore && remaining > 0) {\n const page = yield* journal.entries({\n runId,\n ...(after === undefined ? {} : { after }),\n limit: Math.min(pageSize, remaining)\n }).pipe(Effect.mapError(journalFailure))\n const admitted = yield* Admission.withCommands(page.entries, runId, after ?? -1)\n for (const { command, entry } of admitted) {\n after = entry.seq\n remaining -= 1\n visit(entry, command?.commandId)\n }\n // An empty page ends the walk whatever the page claims, the same\n // guard `SyncServer.tail` carries: `after` cannot move, so the\n // next read is byte-identical and the loop would never terminate\n // \u2014 while holding this branch's admission permit.\n if (page.entries.length === 0) return\n hasMore = page.hasMore\n }\n })\n\n /**\n * Replays the branch journal forward from the last replayed sequence into\n * the ledger. Used once on first touch, so a process that restarts\n * mid-collaboration still recognises every command it already admitted,\n * and again after an admission conflict, to read the command another\n * writer admitted after this process last looked.\n *\n * The staged cursor tracks each visited entry. A successful bounded walk\n * commits exactly where it stopped, so the next replay resumes there.\n * Failure or interruption discards the staged cursor and receipts together.\n */\n const replay = (branchId: BranchId, state: BranchState, budget: number): Effect.Effect<void, SyncError> =>\n Effect.gen(function*() {\n // Stage at most ledgerCapacity receipts. A malformed later page or an\n // interruption must leave both the ledger and replay cursor untouched.\n const staged = new Map(state.receipts)\n let position = state.cursor\n yield* walk(branchId, position, budget, (entry, commandId) => {\n position = entry.seq\n if (commandId !== undefined) {\n recordIn(staged, new CommandReceipt({ branchId, commandId, status: \"admitted\", seq: entry.seq }))\n }\n })\n state.cursor = position\n state.receipts = staged\n })\n\n /**\n * The sequence one command was admitted at, read from the branch's whole\n * durable history rather than from the ledger.\n *\n * The ledger is BOUNDED, so the receipt for a command admitted far\n * enough back has been evicted, and a forward replay from the ledger's\n * own cursor can never bring it back \u2014 the entry is below that cursor.\n * The durable log still holds it, and this is the read that says so.\n * Neither the cursor nor the ledger moves: this answers one question\n * about history and leaves the fast path exactly as it found it.\n */\n const admittedSeq = (\n branchId: BranchId,\n commandId: CommandId\n ): Effect.Effect<JournalEvent.Seq | undefined, SyncError> =>\n Effect.gen(function*() {\n let found: JournalEvent.Seq | undefined\n yield* walk(branchId, undefined, Number.POSITIVE_INFINITY, (entry, admitted) => {\n if (admitted === commandId) found = entry.seq\n })\n return found\n })\n\n /**\n * Seeds the branch's fast path ONCE, from its durable history, within\n * the configured budget (see {@link defaultHydrationLimit}).\n *\n * It runs on the write path, inside the branch's admission permit, so\n * the budget is what keeps the length of a branch's history out of the\n * latency of the next submission to it. The branch is marked hydrated\n * whether or not the walk reached the tail, because the point of the\n * budget is that no submission pays for the rest: what the walk did not\n * reach is answered by the journal, and by the unbounded replay\n * {@link lostRace} runs when the journal reports a conflict.\n */\n const hydrate = (branchId: BranchId, state: BranchState): Effect.Effect<void, SyncError> =>\n Effect.gen(function*() {\n if (state.hydrated) return\n yield* replay(branchId, state, hydrationLimit)\n state.hydrated = true\n })\n\n /**\n * The losing side of a cross-server admission race: the journal refused\n * this append because another writer already holds the command's producer\n * identity with different content \u2014 same `commandId`, different\n * participant or arguments. The original admission is durable, so\n * replaying the branch forward must surface it; a replay that does not is\n * a journal whose conflict report and entries disagree, and that failure\n * is reported honestly instead of being masked as a duplicate.\n *\n * Two reads, because the ledger is bounded and the answer must not be.\n * The forward replay picks up an admission another writer landed after\n * this process last looked. A command this process admitted itself and\n * has since evicted is BELOW that cursor, so only a read of the whole\n * history finds it \u2014 and without one, bounding the ledger turned an\n * ordinary duplicate into a report that the journal contradicts itself.\n */\n const lostRace = (\n submission: CommandSubmission,\n state: BranchState,\n cause: Journal.JournalError\n ): Effect.Effect<CommandReceipt, SyncError> =>\n Effect.gen(function*() {\n // Unbounded, unlike hydration: this is the recovery read, and the\n // sequence it is looking for may sit anywhere in the history.\n yield* replay(submission.branchId, state, Number.POSITIVE_INFINITY)\n const known = state.receipts.get(submission.commandId)\n if (known !== undefined) return duplicateOf(known)\n // A branch still under the ledger's capacity has never evicted\n // anything, and `replay` has just read this branch to its tail, so a\n // command missing from the ledger is missing from the journal too.\n // Reading the whole history to confirm that would let one small\n // request cost one full log scan.\n const mayHaveEvicted = state.receipts.size >= ledgerCapacity\n const seq = mayHaveEvicted\n ? yield* admittedSeq(submission.branchId, submission.commandId)\n : undefined\n if (seq === undefined) return yield* Effect.fail(journalFailure(cause))\n return new CommandReceipt({\n branchId: submission.branchId,\n commandId: submission.commandId,\n status: \"duplicate\",\n seq\n })\n })\n\n const journalInput = (submission: CommandSubmission, capabilityId: string): JournalEvent.Input =>\n new JournalEvent.Input({\n runId: branchRunId(submission.branchId),\n sourceId: commandSourceId(submission.commandId),\n sourceSeq: commandSourceSeq,\n eventType: CommandEvent,\n payload: {\n branchId: submission.branchId,\n commandId: submission.commandId,\n participantId: submission.participantId,\n name: submission.name,\n args: submission.args,\n target: submission.target,\n capabilityId\n },\n meta: null\n })\n\n const admit = (\n request: SubmitRequest,\n capabilityId: string,\n state: BranchState\n ): Effect.Effect<CommandReceipt, SyncError> =>\n Effect.gen(function*() {\n const submission = request.submission\n yield* hydrate(submission.branchId, state)\n const known = state.receipts.get(submission.commandId)\n if (known !== undefined) return duplicateOf(known)\n // Unfenced: the sync command journal is a multi-writer admission\n // log \u2014 participants own no branch run, and command admissions are\n // first-writer-wins on the command id.\n const receipt = yield* journal.emitDurableUnfenced(\n journalInput(submission, capabilityId)\n ).pipe(\n // A `Duplicate` receipt is another writer landing the identical\n // submission first: the journal deduplicated durably and returned\n // the canonical sequence the original append committed at.\n Effect.map((accepted) =>\n new CommandReceipt({\n branchId: submission.branchId,\n commandId: submission.commandId,\n status: accepted._tag === \"Duplicate\" ? \"duplicate\" : \"admitted\",\n seq: accepted.seq\n })\n ),\n Effect.catch((cause) =>\n cause.code === \"idempotency_conflict\"\n ? lostRace(submission, state, cause)\n : Effect.fail(journalFailure(cause))\n )\n )\n recordIn(\n state.receipts,\n new CommandReceipt({\n branchId: submission.branchId,\n commandId: submission.commandId,\n status: \"admitted\",\n seq: receipt.seq\n })\n )\n return receipt\n })\n\n const submit = Effect.fn(\"BranchCommands.submit\")(function*(supplied: SubmitRequest) {\n const decoded = yield* Admission.decode(SubmitRequest, supplied, \"invalid_request\")\n // Freeze the authorized submission across asynchronous signature verification.\n const request = { capability: decoded.capability, submission: { ...decoded.submission } }\n yield* Effect.annotateCurrentSpan({\n branchId: request.submission.branchId,\n commandId: request.submission.commandId,\n participantId: request.submission.participantId\n })\n // The verified capability, not the client's `participantId`, is who\n // submitted: it is stamped on the durable payload for attribution.\n const claims = yield* share.verify(request.capability, {\n branchId: request.submission.branchId,\n access: \"write\"\n })\n const bytes = SyncProtocol.encodedByteLength(request.submission)\n if (bytes > maxCommandBytes) {\n // Refused BEFORE the append: an oversized command must never reach\n // the journal, and therefore never reaches any follower.\n return yield* Effect.fail(\n new SyncError({\n code: \"frame_too_large\",\n message: `Encoded command submission of ${bytes} bytes exceeds the ${maxCommandBytes}-byte ceiling`\n })\n )\n }\n const input = journalInput(request.submission, claims.capabilityId)\n // The journal owns these fields. Reserve their largest encoded forms,\n // so acceptance does not depend on wall clock or current history length.\n // Event identity repeats run/source IDs; their escaping and UTF-8 cost\n // must be measured, not replaced by a fixed envelope allowance.\n const entryBytes = SyncProtocol.encodedByteLength({\n ...input,\n seq: Number.MAX_SAFE_INTEGER - 1,\n eventId: JournalEvent.makeEventId(input.runId, input.sourceId, commandSourceSeq),\n emittedAtMs: Number.MAX_VALUE\n })\n if (entryBytes > maxFrameBytes) {\n return yield* Effect.fail(\n new SyncError({\n code: \"frame_too_large\",\n message:\n `Encoded command journal entry requires up to ${entryBytes} bytes, exceeding the ${maxFrameBytes}-byte frame ceiling`\n })\n )\n }\n // The permit is taken AFTER authorization so an unauthorized caller\n // cannot serialize (and therefore stall) legitimate collaborators,\n // and it is this BRANCH's permit so a slow branch stalls only itself.\n return yield* RcMap.get(branches, request.submission.branchId).pipe(\n Effect.flatMap((state) => state.permit.withPermit(admit(request, claims.capabilityId, state))),\n Effect.scoped\n )\n })\n\n return BranchCommands.of({ submit })\n }\n )\n\nconst defaults = {\n maxCommandBytes: defaultMaxCommandBytes,\n maxFrameBytes: SyncProtocol.defaultMaxFrameBytes,\n ledgerCapacity: defaultLedgerCapacity,\n hydrationLimit: defaultHydrationLimit,\n branchIdleMs: defaultBranchIdleMs\n}\n\n/**\n * Constructs the journal-backed branch command ledger under an explicit\n * policy. The caller scope owns cached states and their idle-expiration timers.\n *\n * A submission whose encoded form exceeds the command ceiling is refused with\n * `frame_too_large` before anything is appended, so one oversized `args`\n * cannot enter the branch journal and poison every follower that replays it.\n *\n * First-touch hydration reads at most `hydrationLimit` entries, so the length\n * of a branch's history is not charged to the next writer's latency; see\n * {@link defaultHydrationLimit} for why the fast path may be seeded partially\n * without any effect on what the ledger admits.\n *\n * Every option is validated as a positive safe integer at construction: the\n * TypeScript type says `number`, and `NaN` silently disabled the ceiling it\n * was compared against.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeLiveWith = (\n options: Options = {}\n): Effect.Effect<Service, SyncError, Journal.Journal | BranchShare.BranchShare | Scope.Scope> =>\n Effect.flatMap(\n Effect.all({\n maxFrameBytes: positiveInt(\n \"BranchCommands.Options.maxFrameBytes\",\n options.maxFrameBytes,\n defaults.maxFrameBytes\n ),\n maxCommandBytes: positiveInt(\n \"BranchCommands.Options.maxCommandBytes\",\n options.maxCommandBytes,\n defaults.maxCommandBytes\n ),\n ledgerCapacity: positiveInt(\n \"BranchCommands.Options.ledgerCapacity\",\n options.ledgerCapacity,\n defaults.ledgerCapacity\n ),\n hydrationLimit: positiveInt(\n \"BranchCommands.Options.hydrationLimit\",\n options.hydrationLimit,\n defaults.hydrationLimit\n ),\n branchIdleMs: positiveInt(\n \"BranchCommands.Options.branchIdleMs\",\n options.branchIdleMs,\n defaults.branchIdleMs\n )\n }),\n makeWith\n )\n\n/**\n * Constructs the journal-backed branch command ledger with default policy.\n * The caller scope owns cached states and their idle-expiration timers.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeLive: Effect.Effect<Service, never, Journal.Journal | BranchShare.BranchShare | Scope.Scope> =\n makeWith(defaults)\n\n/**\n * Provides the journal-backed branch command ledger.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<BranchCommands, never, Journal.Journal | BranchShare.BranchShare> = Layer.effect(\n BranchCommands,\n makeLive\n)\n\n/**\n * Provides the journal-backed branch command ledger under an explicit policy.\n * Fails with `invalid_request` when an option is not a positive safe integer.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerWith = (\n options: Options\n): Layer.Layer<BranchCommands, SyncError, Journal.Journal | BranchShare.BranchShare> =>\n Layer.effect(BranchCommands, makeLiveWith(options))\n\n/**\n * Builds a submission, filling in the fields a plain command never sets, and\n * refusing a field the schema forbids with a `SyncError` rather than a defect.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const submission = (fields: {\n readonly branchId: BranchId\n readonly commandId: CommandId\n readonly participantId: CommandSubmission[\"participantId\"]\n readonly name: string\n readonly args?: string\n readonly target?: string\n}): Effect.Effect<CommandSubmission, SyncError> =>\n // Decoded, not constructed: `name` is a `NonEmptyString` the parameter type\n // admits `\"\"` for, and `new CommandSubmission` throws on it. A builder that\n // dies on a caller's argument is the same defect `submit` already refuses\n // typed, one call earlier.\n Admission.decode(CommandSubmission, {\n branchId: fields.branchId,\n commandId: fields.commandId,\n participantId: fields.participantId,\n name: fields.name,\n args: fields.args ?? \"\",\n target: fields.target ?? \"\"\n }, \"invalid_request\")\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuBA,qBAAwB;AACxB,mBAA8B;AAC9B,cAAyB;AACzB,aAAwB;AACxB,YAAuB;AACvB,YAAuB;AAEvB,gBAA2B;AAC3B,4BAUO;AACP,wBAAmC;AACnC,kBAA6B;AAC7B,gBAA2B;AAC3B,uBAA0B;AAC1B,8BAAiC;AACjC,2BAA4B;AAC5B,uBAA0B;AAC1B,mBAA8B;AAkBvB,MAAM,uBAAuB,QAAQ,QAAiC,EAAE,6BAA6B,EAAE;AAAC;AAQxG,MAAM,WAAW,CAAC,YAA8B,CAAC,MACtD,eAAe,GAAG;AAAA,EAChB,QAAQ,MAAM,OAAO,KAAK,IAAI,2BAAU,EAAE,MAAM,UAAU,SAAS,kCAAkC,CAAC,CAAC;AAAA,EACvG,GAAG;AACL,CAAC;AAQI,MAAM,YAAyC,MAAM,QAAQ,gBAAgB,SAAS,CAAC;AAa9F,MAAM,iBAAiB,CAAC,UACtB,IAAI,2BAAU;AAAA,EACZ,UAAM,0CAAiB,KAAK;AAAA,EAC5B,SAAS;AAAA,EACT,WAAO,4BAAU,KAAK;AACxB,CAAC;AAGH,MAAM,WAAW;AAgBV,MAAM,wBAAwB;AAS9B,MAAM,yBAAyB,OAAO;AAetC,MAAM,sBAAsB;AA8B5B,MAAM,wBAAwB;AAsDrC,MAAM,WAAW,CACf,aAQA,OAAO;AAAA,EACL,aAAY;AACV,UAAM,UAAU,OAAO,uBAAQ;AAC/B,UAAM,QAAQ,OAAO,YAAY;AACjC,UAAM,EAAE,cAAc,gBAAgB,gBAAgB,iBAAiB,cAAc,IAAI;AAEzF,UAAM,WAAW,OAAO,MAAM,KAAK;AAAA,MACjC,gBAAgB;AAAA,MAChB,QAAQ,CAAC,cACP,OAAO,KAAK,OAAoB;AAAA,QAC9B,QAAQ,UAAU,WAAW,CAAC;AAAA,QAC9B,UAAU,oBAAI,IAAI;AAAA,QAClB,QAAQ;AAAA,QACR,UAAU;AAAA,MACZ,EAAE;AAAA,IACN,CAAC;AAQD,UAAM,WAAW,CAAC,QAAwC,YAAkC;AAC1F,aAAO,OAAO,QAAQ,SAAS;AAC/B,aAAO,IAAI,QAAQ,WAAW,OAAO;AACrC,iBAAW,UAAU,OAAO,KAAK,GAAG;AAClC,YAAI,OAAO,QAAQ,eAAgB;AACnC,eAAO,OAAO,MAAM;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,cAAc,CAAC,UACnB,IAAI,qCAAe;AAAA,MACjB,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,QAAQ;AAAA,MACR,KAAK,MAAM;AAAA,IACb,CAAC;AAeH,UAAM,OAAO,CACX,UACA,MACA,QACA,UAEA,OAAO,IAAI,aAAY;AACrB,YAAM,YAAQ,mCAAY,QAAQ;AAClC,UAAI,QAAQ;AACZ,UAAI,UAAU;AACd,UAAI,YAAY;AAChB,aAAO,WAAW,YAAY,GAAG;AAC/B,cAAM,OAAO,OAAO,QAAQ,QAAQ;AAAA,UAClC;AAAA,UACA,GAAI,UAAU,SAAY,CAAC,IAAI,EAAE,MAAM;AAAA,UACvC,OAAO,KAAK,IAAI,UAAU,SAAS;AAAA,QACrC,CAAC,EAAE,KAAK,OAAO,SAAS,cAAc,CAAC;AACvC,cAAM,WAAW,OAAO,UAAU,aAAa,KAAK,SAAS,OAAO,SAAS,EAAE;AAC/E,mBAAW,EAAE,SAAS,MAAM,KAAK,UAAU;AACzC,kBAAQ,MAAM;AACd,uBAAa;AACb,gBAAM,OAAO,SAAS,SAAS;AAAA,QACjC;AAKA,YAAI,KAAK,QAAQ,WAAW,EAAG;AAC/B,kBAAU,KAAK;AAAA,MACjB;AAAA,IACF,CAAC;AAaH,UAAM,SAAS,CAAC,UAAoB,OAAoB,WACtD,OAAO,IAAI,aAAY;AAGrB,YAAM,SAAS,IAAI,IAAI,MAAM,QAAQ;AACrC,UAAI,WAAW,MAAM;AACrB,aAAO,KAAK,UAAU,UAAU,QAAQ,CAAC,OAAO,cAAc;AAC5D,mBAAW,MAAM;AACjB,YAAI,cAAc,QAAW;AAC3B,mBAAS,QAAQ,IAAI,qCAAe,EAAE,UAAU,WAAW,QAAQ,YAAY,KAAK,MAAM,IAAI,CAAC,CAAC;AAAA,QAClG;AAAA,MACF,CAAC;AACD,YAAM,SAAS;AACf,YAAM,WAAW;AAAA,IACnB,CAAC;AAaH,UAAM,cAAc,CAClB,UACA,cAEA,OAAO,IAAI,aAAY;AACrB,UAAI;AACJ,aAAO,KAAK,UAAU,QAAW,OAAO,mBAAmB,CAAC,OAAO,aAAa;AAC9E,YAAI,aAAa,UAAW,SAAQ,MAAM;AAAA,MAC5C,CAAC;AACD,aAAO;AAAA,IACT,CAAC;AAcH,UAAM,UAAU,CAAC,UAAoB,UACnC,OAAO,IAAI,aAAY;AACrB,UAAI,MAAM,SAAU;AACpB,aAAO,OAAO,UAAU,OAAO,cAAc;AAC7C,YAAM,WAAW;AAAA,IACnB,CAAC;AAkBH,UAAM,WAAW,CACfA,aACA,OACA,UAEA,OAAO,IAAI,aAAY;AAGrB,aAAO,OAAOA,YAAW,UAAU,OAAO,OAAO,iBAAiB;AAClE,YAAM,QAAQ,MAAM,SAAS,IAAIA,YAAW,SAAS;AACrD,UAAI,UAAU,OAAW,QAAO,YAAY,KAAK;AAMjD,YAAM,iBAAiB,MAAM,SAAS,QAAQ;AAC9C,YAAM,MAAM,iBACR,OAAO,YAAYA,YAAW,UAAUA,YAAW,SAAS,IAC5D;AACJ,UAAI,QAAQ,OAAW,QAAO,OAAO,OAAO,KAAK,eAAe,KAAK,CAAC;AACtE,aAAO,IAAI,qCAAe;AAAA,QACxB,UAAUA,YAAW;AAAA,QACrB,WAAWA,YAAW;AAAA,QACtB,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAEH,UAAM,eAAe,CAACA,aAA+B,iBACnD,IAAI,aAAa,MAAM;AAAA,MACrB,WAAO,mCAAYA,YAAW,QAAQ;AAAA,MACtC,cAAU,uCAAgBA,YAAW,SAAS;AAAA,MAC9C,WAAW;AAAA,MACX,WAAW;AAAA,MACX,SAAS;AAAA,QACP,UAAUA,YAAW;AAAA,QACrB,WAAWA,YAAW;AAAA,QACtB,eAAeA,YAAW;AAAA,QAC1B,MAAMA,YAAW;AAAA,QACjB,MAAMA,YAAW;AAAA,QACjB,QAAQA,YAAW;AAAA,QACnB;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAEH,UAAM,QAAQ,CACZ,SACA,cACA,UAEA,OAAO,IAAI,aAAY;AACrB,YAAMA,cAAa,QAAQ;AAC3B,aAAO,QAAQA,YAAW,UAAU,KAAK;AACzC,YAAM,QAAQ,MAAM,SAAS,IAAIA,YAAW,SAAS;AACrD,UAAI,UAAU,OAAW,QAAO,YAAY,KAAK;AAIjD,YAAM,UAAU,OAAO,QAAQ;AAAA,QAC7B,aAAaA,aAAY,YAAY;AAAA,MACvC,EAAE;AAAA;AAAA;AAAA;AAAA,QAIA,OAAO;AAAA,UAAI,CAAC,aACV,IAAI,qCAAe;AAAA,YACjB,UAAUA,YAAW;AAAA,YACrB,WAAWA,YAAW;AAAA,YACtB,QAAQ,SAAS,SAAS,cAAc,cAAc;AAAA,YACtD,KAAK,SAAS;AAAA,UAChB,CAAC;AAAA,QACH;AAAA,QACA,OAAO;AAAA,UAAM,CAAC,UACZ,MAAM,SAAS,yBACX,SAASA,aAAY,OAAO,KAAK,IACjC,OAAO,KAAK,eAAe,KAAK,CAAC;AAAA,QACvC;AAAA,MACF;AACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI,qCAAe;AAAA,UACjB,UAAUA,YAAW;AAAA,UACrB,WAAWA,YAAW;AAAA,UACtB,QAAQ;AAAA,UACR,KAAK,QAAQ;AAAA,QACf,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT,CAAC;AAEH,UAAM,SAAS,OAAO,GAAG,uBAAuB,EAAE,WAAU,UAAyB;AACnF,YAAM,UAAU,OAAO,UAAU,OAAO,qCAAe,UAAU,iBAAiB;AAElF,YAAM,UAAU,EAAE,YAAY,QAAQ,YAAY,YAAY,EAAE,GAAG,QAAQ,WAAW,EAAE;AACxF,aAAO,OAAO,oBAAoB;AAAA,QAChC,UAAU,QAAQ,WAAW;AAAA,QAC7B,WAAW,QAAQ,WAAW;AAAA,QAC9B,eAAe,QAAQ,WAAW;AAAA,MACpC,CAAC;AAGD,YAAM,SAAS,OAAO,MAAM,OAAO,QAAQ,YAAY;AAAA,QACrD,UAAU,QAAQ,WAAW;AAAA,QAC7B,QAAQ;AAAA,MACV,CAAC;AACD,YAAM,QAAQ,aAAa,kBAAkB,QAAQ,UAAU;AAC/D,UAAI,QAAQ,iBAAiB;AAG3B,eAAO,OAAO,OAAO;AAAA,UACnB,IAAI,2BAAU;AAAA,YACZ,MAAM;AAAA,YACN,SAAS,iCAAiC,KAAK,sBAAsB,eAAe;AAAA,UACtF,CAAC;AAAA,QACH;AAAA,MACF;AACA,YAAM,QAAQ,aAAa,QAAQ,YAAY,OAAO,YAAY;AAKlE,YAAM,aAAa,aAAa,kBAAkB;AAAA,QAChD,GAAG;AAAA,QACH,KAAK,OAAO,mBAAmB;AAAA,QAC/B,SAAS,aAAa,YAAY,MAAM,OAAO,MAAM,UAAU,sCAAgB;AAAA,QAC/E,aAAa,OAAO;AAAA,MACtB,CAAC;AACD,UAAI,aAAa,eAAe;AAC9B,eAAO,OAAO,OAAO;AAAA,UACnB,IAAI,2BAAU;AAAA,YACZ,MAAM;AAAA,YACN,SACE,gDAAgD,UAAU,yBAAyB,aAAa;AAAA,UACpG,CAAC;AAAA,QACH;AAAA,MACF;AAIA,aAAO,OAAO,MAAM,IAAI,UAAU,QAAQ,WAAW,QAAQ,EAAE;AAAA,QAC7D,OAAO,QAAQ,CAAC,UAAU,MAAM,OAAO,WAAW,MAAM,SAAS,OAAO,cAAc,KAAK,CAAC,CAAC;AAAA,QAC7F,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAED,WAAO,eAAe,GAAG,EAAE,OAAO,CAAC;AAAA,EACrC;AACF;AAEF,MAAM,WAAW;AAAA,EACf,iBAAiB;AAAA,EACjB,eAAe,aAAa;AAAA,EAC5B,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,cAAc;AAChB;AAsBO,MAAM,eAAe,CAC1B,UAAmB,CAAC,MAEpB,OAAO;AAAA,EACL,OAAO,IAAI;AAAA,IACT,mBAAe;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,qBAAiB;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,oBAAgB;AAAA,MACd;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,oBAAgB;AAAA,MACd;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,IACA,kBAAc;AAAA,MACZ;AAAA,MACA,QAAQ;AAAA,MACR,SAAS;AAAA,IACX;AAAA,EACF,CAAC;AAAA,EACD;AACF;AASK,MAAM,WACX,SAAS,QAAQ;AAQZ,MAAM,QAAuF,MAAM;AAAA,EACxG;AAAA,EACA;AACF;AASO,MAAM,YAAY,CACvB,YAEA,MAAM,OAAO,gBAAgB,aAAa,OAAO,CAAC;AAS7C,MAAM,aAAa,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,EAYzB,UAAU,OAAO,yCAAmB;AAAA,IAClC,UAAU,OAAO;AAAA,IACjB,WAAW,OAAO;AAAA,IAClB,eAAe,OAAO;AAAA,IACtB,MAAM,OAAO;AAAA,IACb,MAAM,OAAO,QAAQ;AAAA,IACrB,QAAQ,OAAO,UAAU;AAAA,EAC3B,GAAG,iBAAiB;AAAA;",
6
+ "names": ["submission"]
7
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The source of freshly minted branch and capability identifiers.
3
+ *
4
+ * Minting an id is nondeterminism against the host, so it enters through a
5
+ * port like every other host effect: `BranchServer.layerHandlers` asks
6
+ * this service for the ids it puts on the wire instead of calling a global
7
+ * generator, which is what lets a deterministic composition — a test, a
8
+ * replayed transcript, a deployment that derives ids from its own namespace —
9
+ * supply its own without touching the handlers.
10
+ *
11
+ * @since 0.1.0
12
+ */
13
+ import * as Context from "effect/Context";
14
+ import * as Effect from "effect/Effect";
15
+ import * as Layer from "effect/Layer";
16
+ /**
17
+ * Identifier minting operations.
18
+ *
19
+ * @category models
20
+ * @since 0.1.0
21
+ */
22
+ export interface Service {
23
+ /** A fresh, unguessable identifier. Never returns the same value twice. */
24
+ readonly fresh: Effect.Effect<string>;
25
+ }
26
+ declare const BranchIds_base: Context.ServiceClass<BranchIds, "@smthrs/sync/BranchIds", Service>;
27
+ /**
28
+ * The branch identifier source.
29
+ *
30
+ * @category services
31
+ * @since 0.1.0
32
+ */
33
+ export declare class BranchIds extends BranchIds_base {
34
+ }
35
+ /**
36
+ * Constructs the default source: Web Crypto UUIDs.
37
+ *
38
+ * Web Crypto is used directly, as it is for the share signature in
39
+ * `BranchShare`, so the same module runs in the browser and on node.
40
+ * A branch id is not itself an authorization — every operation still
41
+ * authorizes through a signed capability — but an unguessable id keeps a
42
+ * branch from being enumerated, which is why the default stays with a
43
+ * cryptographic generator rather than `Random`.
44
+ *
45
+ * @category constructors
46
+ * @since 0.1.0
47
+ */
48
+ export declare const makeWebCrypto: () => Service;
49
+ /**
50
+ * Provides {@link makeWebCrypto}.
51
+ *
52
+ * @category layers
53
+ * @since 0.1.0
54
+ */
55
+ export declare const layer: Layer.Layer<BranchIds>;
56
+ /**
57
+ * Provides a deterministic counter, `<prefix>-1`, `<prefix>-2`, and so on.
58
+ * For tests and reproducible transcripts only — the ids are guessable.
59
+ *
60
+ * @category layers
61
+ * @since 0.1.0
62
+ */
63
+ export declare const layerSequential: (prefix: string) => Layer.Layer<BranchIds>;
64
+ export {};
65
+ //# sourceMappingURL=BranchIds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BranchIds.d.ts","sourceRoot":"","sources":["../../src/BranchIds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;CACtC;;AAED;;;;;GAKG;AACH,qBAAa,SAAU,SAAQ,cAA+D;CAAG;AAEjG;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,QAAO,OAA0E,CAAA;AAE3G;;;;;GAKG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS,CAAwC,CAAA;AAEjF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,KAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CASlE,CAAA"}
@@ -0,0 +1,53 @@
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 BranchIds_exports = {};
30
+ __export(BranchIds_exports, {
31
+ BranchIds: () => BranchIds,
32
+ layer: () => layer,
33
+ layerSequential: () => layerSequential,
34
+ makeWebCrypto: () => makeWebCrypto
35
+ });
36
+ module.exports = __toCommonJS(BranchIds_exports);
37
+ var Context = __toESM(require("effect/Context"), 1);
38
+ var Effect = __toESM(require("effect/Effect"), 1);
39
+ var Layer = __toESM(require("effect/Layer"), 1);
40
+ class BranchIds extends Context.Service()("@smthrs/sync/BranchIds") {
41
+ }
42
+ const makeWebCrypto = () => BranchIds.of({ fresh: Effect.sync(() => crypto.randomUUID()) });
43
+ const layer = Layer.sync(BranchIds)(makeWebCrypto);
44
+ const layerSequential = (prefix) => Layer.sync(BranchIds)(() => {
45
+ let sequence = 0;
46
+ return BranchIds.of({
47
+ fresh: Effect.sync(() => {
48
+ sequence += 1;
49
+ return `${prefix}-${sequence}`;
50
+ })
51
+ });
52
+ });
53
+ //# sourceMappingURL=BranchIds.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/BranchIds.ts"],
4
+ "sourcesContent": ["/**\n * The source of freshly minted branch and capability identifiers.\n *\n * Minting an id is nondeterminism against the host, so it enters through a\n * port like every other host effect: `BranchServer.layerHandlers` asks\n * this service for the ids it puts on the wire instead of calling a global\n * generator, which is what lets a deterministic composition \u2014 a test, a\n * replayed transcript, a deployment that derives ids from its own namespace \u2014\n * supply its own without touching the handlers.\n *\n * @since 0.1.0\n */\n\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\n\n/**\n * Identifier minting operations.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n /** A fresh, unguessable identifier. Never returns the same value twice. */\n readonly fresh: Effect.Effect<string>\n}\n\n/**\n * The branch identifier source.\n *\n * @category services\n * @since 0.1.0\n */\nexport class BranchIds extends Context.Service<BranchIds, Service>()(\"@smthrs/sync/BranchIds\") {}\n\n/**\n * Constructs the default source: Web Crypto UUIDs.\n *\n * Web Crypto is used directly, as it is for the share signature in\n * `BranchShare`, so the same module runs in the browser and on node.\n * A branch id is not itself an authorization \u2014 every operation still\n * authorizes through a signed capability \u2014 but an unguessable id keeps a\n * branch from being enumerated, which is why the default stays with a\n * cryptographic generator rather than `Random`.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeWebCrypto = (): Service => BranchIds.of({ fresh: Effect.sync(() => crypto.randomUUID()) })\n\n/**\n * Provides {@link makeWebCrypto}.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<BranchIds> = Layer.sync(BranchIds)(makeWebCrypto)\n\n/**\n * Provides a deterministic counter, `<prefix>-1`, `<prefix>-2`, and so on.\n * For tests and reproducible transcripts only \u2014 the ids are guessable.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerSequential = (prefix: string): Layer.Layer<BranchIds> =>\n Layer.sync(BranchIds)(() => {\n let sequence = 0\n return BranchIds.of({\n fresh: Effect.sync(() => {\n sequence += 1\n return `${prefix}-${sequence}`\n })\n })\n })\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaA,cAAyB;AACzB,aAAwB;AACxB,YAAuB;AAmBhB,MAAM,kBAAkB,QAAQ,QAA4B,EAAE,wBAAwB,EAAE;AAAC;AAezF,MAAM,gBAAgB,MAAe,UAAU,GAAG,EAAE,OAAO,OAAO,KAAK,MAAM,OAAO,WAAW,CAAC,EAAE,CAAC;AAQnG,MAAM,QAAgC,MAAM,KAAK,SAAS,EAAE,aAAa;AASzE,MAAM,kBAAkB,CAAC,WAC9B,MAAM,KAAK,SAAS,EAAE,MAAM;AAC1B,MAAI,WAAW;AACf,SAAO,UAAU,GAAG;AAAA,IAClB,OAAO,OAAO,KAAK,MAAM;AACvB,kBAAY;AACZ,aAAO,GAAG,MAAM,IAAI,QAAQ;AAAA,IAC9B,CAAC;AAAA,EACH,CAAC;AACH,CAAC;",
6
+ "names": []
7
+ }