@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,147 @@
1
+ /**
2
+ * Stable, schema-backed failures for the sync boundary.
3
+ *
4
+ * @since 0.1.0
5
+ */
6
+
7
+ import * as JournalEvent from "@smthrs/journal/JournalEvent"
8
+ import * as Predicate from "effect/Predicate"
9
+ import * as Schema from "effect/Schema"
10
+ import { Resync } from "./SyncProtocol.ts"
11
+
12
+ /** The literals behind {@link ErrorCode}, also read by {@link SyncError.is}. */
13
+ const errorCodes = [
14
+ "invalid_request",
15
+ "unsupported",
16
+ "unauthorized",
17
+ "not_found",
18
+ "backpressure",
19
+ "frame_too_large",
20
+ "decode_failed",
21
+ "transport_failed",
22
+ "protocol_violation",
23
+ "lineage_changed",
24
+ "compacted",
25
+ "closed",
26
+ "unknown"
27
+ ] as const
28
+
29
+ /**
30
+ * Stable error codes returned by sync operations.
31
+ *
32
+ * `unsupported` reports an unavailable host operation or presence transport
33
+ * (since 1.0.0). It never implies an empty roster.
34
+ *
35
+ * `compacted` reports that the request's cursor for one run starts below that
36
+ * run's compaction floor, so the entries it asks for have been deleted. It is
37
+ * its own code rather than an `unknown` because it is RECOVERABLE and nothing
38
+ * else here is: the accompanying {@link SyncError.resync} names the checkpoint
39
+ * to resume from, and a follower that applies it converges. Folding it into
40
+ * `unknown` cost the checkpoint, and with it every path back.
41
+ *
42
+ * @category models
43
+ * @since 0.1.0
44
+ */
45
+ export const ErrorCode = Schema.Literals(errorCodes).annotate({ identifier: "@smthrs/sync/ErrorCode" })
46
+
47
+ const codes: ReadonlySet<string> = new Set(errorCodes)
48
+
49
+ /**
50
+ * Stable error code returned by a sync operation.
51
+ *
52
+ * @category models
53
+ * @since 0.1.0
54
+ */
55
+ export type ErrorCode = typeof ErrorCode.Type
56
+
57
+ const Rewind = Schema.Struct({
58
+ runId: JournalEvent.RunId,
59
+ generation: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)),
60
+ afterSeq: Schema.Int.check(Schema.isGreaterThanOrEqualTo(-1))
61
+ })
62
+
63
+ /**
64
+ * Error raised by sync validation, transport, framing, or subscription work.
65
+ *
66
+ * `resync` is set only on `compacted`, and names the run and the checkpoint
67
+ * sequence a follower resumes from. It is optional so every other code keeps
68
+ * the shape it already had on the wire.
69
+ *
70
+ * `rewind` is set on server-side `lineage_changed` refusals and names the
71
+ * current generation and archive boundary. The caller rebuilds its projection
72
+ * from retained history and creates a fresh client; the client never retries this failure.
73
+ *
74
+ * `cause` is a bounded STRING, not the host object that failed. This error is
75
+ * the declared error schema of every RPC in both groups, so whatever it
76
+ * carries reaches a remote follower that may hold nothing but a branch share
77
+ * link. A `Schema.Unknown` cause published the host's own failure verbatim —
78
+ * a driver message with SQL text, a rejected credential — counted against no
79
+ * size ceiling, and had no defined wire form for a class instance or a cyclic
80
+ * value. `internal/CauseText` is the one renderer that fills it.
81
+ *
82
+ * @category errors
83
+ * @since 0.1.0
84
+ */
85
+ export class SyncError extends Schema.TaggedError<SyncError>()("@smthrs/sync/SyncError", {
86
+ code: ErrorCode,
87
+ message: Schema.String,
88
+ cause: Schema.optional(Schema.String),
89
+ resync: Schema.optional(Resync),
90
+ rewind: Schema.optional(Rewind)
91
+ }) {
92
+ /**
93
+ * Returns `true` when a value carries this error's shape: the tag, a `code`
94
+ * this package declares, a string `message`, and a `resync` only alongside
95
+ * `compacted`.
96
+ *
97
+ * It is a STRUCTURAL check, not a class check, deliberately. Everything that
98
+ * reaches it on a shipped path has crossed a boundary that rebuilds the
99
+ * value — the RPC client's schema-decoded error channel, or a browser
100
+ * `postMessage` that keeps the fields and drops the prototype — and a check
101
+ * that demanded the prototype would refuse an error the package itself sent.
102
+ * What it does not verify is that the value was produced by this package: a
103
+ * value constructed in-process with the right fields passes.
104
+ *
105
+ * It is TOTAL. The value is `unknown`, so ANY property read on it may be a
106
+ * throwing getter — the tag included, which is the first one read and was
107
+ * the one read outside the protection — and this guard decides whether a
108
+ * follow reconnects and whether a cursor moves past a compaction floor: a
109
+ * question about a value's shape must answer, never raise.
110
+ *
111
+ * @since 0.1.0
112
+ */
113
+ static readonly is = (value: unknown): value is SyncError => {
114
+ try {
115
+ if (!Predicate.isTagged(value, "@smthrs/sync/SyncError")) return false
116
+ const candidate = value as {
117
+ readonly code?: unknown
118
+ readonly message?: unknown
119
+ readonly resync?: unknown
120
+ readonly rewind?: unknown
121
+ }
122
+ if (typeof candidate.message !== "string") return false
123
+ if (typeof candidate.code !== "string" || !codes.has(candidate.code)) return false
124
+ if (
125
+ candidate.rewind !== undefined &&
126
+ (candidate.code !== "lineage_changed" || !Schema.is(Rewind)(candidate.rewind))
127
+ ) return false
128
+ if (candidate.resync === undefined) return true
129
+ return candidate.code === "compacted" && Schema.is(Resync)(candidate.resync)
130
+ } catch {
131
+ return false
132
+ }
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Terminal error raised when a server frame starts beyond the client's
138
+ * covered cursor.
139
+ *
140
+ * @category errors
141
+ * @since 0.1.0
142
+ */
143
+ export class SyncGapError extends Schema.TaggedError<SyncGapError>()("@smthrs/sync/SyncGapError", {
144
+ runId: JournalEvent.RunId,
145
+ expectedFrom: JournalEvent.Seq,
146
+ receivedFrom: JournalEvent.Seq
147
+ }) {}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The authenticated identity of one sync request.
3
+ *
4
+ * The principal is a `Context.Reference` whose default is
5
+ * {@link anonymous}, which is what makes the read path fail closed: code
6
+ * that never authenticates runs as anonymous, and the server refuses
7
+ * anonymous access to every non-branch run. The only ways to become the
8
+ * workspace principal are a workspace capability verified by
9
+ * `SyncAuth.layer` at the RPC boundary, or an in-process caller that
10
+ * provides the reference itself — which is the sanctioned path for code
11
+ * already holding the journal, and never for a transport.
12
+ *
13
+ * @since 0.1.0
14
+ */
15
+
16
+ import * as Context from "effect/Context"
17
+ import * as Layer from "effect/Layer"
18
+
19
+ /**
20
+ * A request that presented no credential. It may still read branch runs
21
+ * through per-request branch share capabilities.
22
+ *
23
+ * @category models
24
+ * @since 0.1.0
25
+ */
26
+ export interface Anonymous {
27
+ readonly _tag: "Anonymous"
28
+ }
29
+
30
+ /**
31
+ * A request authenticated for the workspace's non-branch runs.
32
+ * `capabilityId` names the credential for auditing; in-process callers name
33
+ * themselves.
34
+ *
35
+ * @category models
36
+ * @since 0.1.0
37
+ */
38
+ export interface Workspace {
39
+ readonly _tag: "Workspace"
40
+ readonly capabilityId: string
41
+ /**
42
+ * When the credential that established this principal stops authorizing,
43
+ * in epoch milliseconds, or `Infinity` for an in-process owner that
44
+ * presented no credential at all.
45
+ *
46
+ * A signed expiry is this package's ONLY revocation mechanism, and an open
47
+ * subscription is not a sequence of requests: it is authorized once, at
48
+ * open, and then streams. Without the expiry on the identity itself,
49
+ * nothing downstream could tell a live subscription to stop, so a holder of
50
+ * an expired capability kept reading for as long as it declined to
51
+ * disconnect.
52
+ */
53
+ readonly expiresAtMs: number
54
+ }
55
+
56
+ /**
57
+ * Any authenticated identity a sync request can carry.
58
+ *
59
+ * @category models
60
+ * @since 0.1.0
61
+ */
62
+ export type Principal = Anonymous | Workspace
63
+
64
+ /**
65
+ * The unauthenticated principal.
66
+ *
67
+ * @category constructors
68
+ * @since 0.1.0
69
+ */
70
+ export const anonymous: Principal = { _tag: "Anonymous" }
71
+
72
+ /**
73
+ * Constructs the workspace principal for a named credential.
74
+ *
75
+ * `expiresAtMs` defaults to `Infinity`, which is the honest answer for an
76
+ * in-process owner that already holds the journal: it presented no credential,
77
+ * so no credential can expire under it.
78
+ *
79
+ * @category constructors
80
+ * @since 0.1.0
81
+ */
82
+ export const workspace = (
83
+ capabilityId: string,
84
+ expiresAtMs: number = Number.POSITIVE_INFINITY
85
+ ): Principal => ({ _tag: "Workspace", capabilityId, expiresAtMs })
86
+
87
+ /**
88
+ * Returns `true` when a principal may read non-branch runs.
89
+ *
90
+ * @category predicates
91
+ * @since 0.1.0
92
+ */
93
+ export const isWorkspace = (principal: Principal): principal is Workspace => principal._tag === "Workspace"
94
+
95
+ /**
96
+ * The current request's principal, defaulting to {@link anonymous}.
97
+ *
98
+ * @category references
99
+ * @since 0.1.0
100
+ */
101
+ export const SyncPrincipal: Context.Reference<Principal> = Context.Reference<Principal>(
102
+ "@smthrs/sync/SyncPrincipal",
103
+ { defaultValue: () => anonymous }
104
+ )
105
+
106
+ /**
107
+ * Provides the workspace principal directly, bypassing credential
108
+ * verification. For in-process callers that already own the journal — a
109
+ * transport boundary must authenticate through `SyncAuth.layer` instead.
110
+ *
111
+ * @category layers
112
+ * @since 0.1.0
113
+ */
114
+ export const layerWorkspace = (capabilityId: string): Layer.Layer<never> =>
115
+ Layer.succeed(SyncPrincipal, workspace(capabilityId))