@smthrs/sync 0.0.0-stage → 1.0.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +208 -0
- package/LICENSE +21 -0
- package/README.md +208 -2
- package/dist/cjs/BranchCommands.d.ts +228 -0
- package/dist/cjs/BranchCommands.d.ts.map +1 -0
- package/dist/cjs/BranchCommands.js +316 -0
- package/dist/cjs/BranchCommands.js.map +7 -0
- package/dist/cjs/BranchIds.d.ts +65 -0
- package/dist/cjs/BranchIds.d.ts.map +1 -0
- package/dist/cjs/BranchIds.js +53 -0
- package/dist/cjs/BranchIds.js.map +7 -0
- package/dist/cjs/BranchPresence.d.ts +195 -0
- package/dist/cjs/BranchPresence.d.ts.map +1 -0
- package/dist/cjs/BranchPresence.js +276 -0
- package/dist/cjs/BranchPresence.js.map +7 -0
- package/dist/cjs/BranchProjection.d.ts +179 -0
- package/dist/cjs/BranchProjection.d.ts.map +1 -0
- package/dist/cjs/BranchProjection.js +197 -0
- package/dist/cjs/BranchProjection.js.map +7 -0
- package/dist/cjs/BranchProtocol.d.ts +415 -0
- package/dist/cjs/BranchProtocol.d.ts.map +1 -0
- package/dist/cjs/BranchProtocol.js +172 -0
- package/dist/cjs/BranchProtocol.js.map +7 -0
- package/dist/cjs/BranchRpcs.d.ts +177 -0
- package/dist/cjs/BranchRpcs.d.ts.map +1 -0
- package/dist/cjs/BranchRpcs.js +85 -0
- package/dist/cjs/BranchRpcs.js.map +7 -0
- package/dist/cjs/BranchServer.d.ts +55 -0
- package/dist/cjs/BranchServer.d.ts.map +1 -0
- package/dist/cjs/BranchServer.js +147 -0
- package/dist/cjs/BranchServer.js.map +7 -0
- package/dist/cjs/BranchShare.d.ts +159 -0
- package/dist/cjs/BranchShare.d.ts.map +1 -0
- package/dist/cjs/BranchShare.js +147 -0
- package/dist/cjs/BranchShare.js.map +7 -0
- package/dist/cjs/RunCatalog.d.ts +207 -0
- package/dist/cjs/RunCatalog.d.ts.map +1 -0
- package/dist/cjs/RunCatalog.js +122 -0
- package/dist/cjs/RunCatalog.js.map +7 -0
- package/dist/cjs/SyncAuth.d.ts +70 -0
- package/dist/cjs/SyncAuth.d.ts.map +1 -0
- package/dist/cjs/SyncAuth.js +109 -0
- package/dist/cjs/SyncAuth.js.map +7 -0
- package/dist/cjs/SyncClient.d.ts +274 -0
- package/dist/cjs/SyncClient.d.ts.map +1 -0
- package/dist/cjs/SyncClient.js +474 -0
- package/dist/cjs/SyncClient.js.map +7 -0
- package/dist/cjs/SyncError.d.ts +107 -0
- package/dist/cjs/SyncError.d.ts.map +1 -0
- package/dist/cjs/SyncError.js +110 -0
- package/dist/cjs/SyncError.js.map +7 -0
- package/dist/cjs/SyncPrincipal.d.ts +100 -0
- package/dist/cjs/SyncPrincipal.d.ts.map +1 -0
- package/dist/cjs/SyncPrincipal.js +48 -0
- package/dist/cjs/SyncPrincipal.js.map +7 -0
- package/dist/cjs/SyncProtocol.d.ts +592 -0
- package/dist/cjs/SyncProtocol.d.ts.map +1 -0
- package/dist/cjs/SyncProtocol.js +165 -0
- package/dist/cjs/SyncProtocol.js.map +7 -0
- package/dist/cjs/SyncRpcs.d.ts +92 -0
- package/dist/cjs/SyncRpcs.d.ts.map +1 -0
- package/dist/cjs/SyncRpcs.js +35 -0
- package/dist/cjs/SyncRpcs.js.map +7 -0
- package/dist/cjs/SyncServer.d.ts +184 -0
- package/dist/cjs/SyncServer.d.ts.map +1 -0
- package/dist/cjs/SyncServer.js +513 -0
- package/dist/cjs/SyncServer.js.map +7 -0
- package/dist/cjs/WorkspaceShare.d.ts +174 -0
- package/dist/cjs/WorkspaceShare.d.ts.map +1 -0
- package/dist/cjs/WorkspaceShare.js +149 -0
- package/dist/cjs/WorkspaceShare.js.map +7 -0
- package/dist/cjs/index.d.ts +91 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +67 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/internal/Admission.d.ts +43 -0
- package/dist/cjs/internal/Admission.d.ts.map +1 -0
- package/dist/cjs/internal/Admission.js +90 -0
- package/dist/cjs/internal/Admission.js.map +7 -0
- package/dist/cjs/internal/CauseText.d.ts +55 -0
- package/dist/cjs/internal/CauseText.d.ts.map +1 -0
- package/dist/cjs/internal/CauseText.js +38 -0
- package/dist/cjs/internal/CauseText.js.map +7 -0
- package/dist/cjs/internal/JournalErrorCode.d.ts +23 -0
- package/dist/cjs/internal/JournalErrorCode.d.ts.map +1 -0
- package/dist/cjs/internal/JournalErrorCode.js +34 -0
- package/dist/cjs/internal/JournalErrorCode.js.map +7 -0
- package/dist/cjs/internal/PolicyOptions.d.ts +57 -0
- package/dist/cjs/internal/PolicyOptions.d.ts.map +1 -0
- package/dist/cjs/internal/PolicyOptions.js +62 -0
- package/dist/cjs/internal/PolicyOptions.js.map +7 -0
- package/dist/cjs/internal/ProtocolVersion.d.ts +17 -0
- package/dist/cjs/internal/ProtocolVersion.d.ts.map +1 -0
- package/dist/cjs/internal/ProtocolVersion.js +44 -0
- package/dist/cjs/internal/ProtocolVersion.js.map +7 -0
- package/dist/cjs/internal/ShareSigner.d.ts +173 -0
- package/dist/cjs/internal/ShareSigner.d.ts.map +1 -0
- package/dist/cjs/internal/ShareSigner.js +150 -0
- package/dist/cjs/internal/ShareSigner.js.map +7 -0
- package/dist/cjs/internal/SnapshotBoundary.d.ts +17 -0
- package/dist/cjs/internal/SnapshotBoundary.d.ts.map +1 -0
- package/dist/cjs/internal/SnapshotBoundary.js +97 -0
- package/dist/cjs/internal/SnapshotBoundary.js.map +7 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/test/TestSocket.d.ts +55 -0
- package/dist/cjs/test/TestSocket.d.ts.map +1 -0
- package/dist/cjs/test/TestSocket.js +145 -0
- package/dist/cjs/test/TestSocket.js.map +7 -0
- package/dist/cjs/test/TestSync.d.ts +54 -0
- package/dist/cjs/test/TestSync.d.ts.map +1 -0
- package/dist/cjs/test/TestSync.js +96 -0
- package/dist/cjs/test/TestSync.js.map +7 -0
- package/dist/esm/BranchCommands.d.ts +228 -0
- package/dist/esm/BranchCommands.d.ts.map +1 -0
- package/dist/esm/BranchCommands.js +496 -0
- package/dist/esm/BranchCommands.js.map +1 -0
- package/dist/esm/BranchIds.d.ts +65 -0
- package/dist/esm/BranchIds.d.ts.map +1 -0
- package/dist/esm/BranchIds.js +61 -0
- package/dist/esm/BranchIds.js.map +1 -0
- package/dist/esm/BranchPresence.d.ts +195 -0
- package/dist/esm/BranchPresence.d.ts.map +1 -0
- package/dist/esm/BranchPresence.js +361 -0
- package/dist/esm/BranchPresence.js.map +1 -0
- package/dist/esm/BranchProjection.d.ts +179 -0
- package/dist/esm/BranchProjection.d.ts.map +1 -0
- package/dist/esm/BranchProjection.js +269 -0
- package/dist/esm/BranchProjection.js.map +1 -0
- package/dist/esm/BranchProtocol.d.ts +415 -0
- package/dist/esm/BranchProtocol.d.ts.map +1 -0
- package/dist/esm/BranchProtocol.js +323 -0
- package/dist/esm/BranchProtocol.js.map +1 -0
- package/dist/esm/BranchRpcs.d.ts +177 -0
- package/dist/esm/BranchRpcs.d.ts.map +1 -0
- package/dist/esm/BranchRpcs.js +103 -0
- package/dist/esm/BranchRpcs.js.map +1 -0
- package/dist/esm/BranchServer.d.ts +55 -0
- package/dist/esm/BranchServer.d.ts.map +1 -0
- package/dist/esm/BranchServer.js +168 -0
- package/dist/esm/BranchServer.js.map +1 -0
- package/dist/esm/BranchShare.d.ts +159 -0
- package/dist/esm/BranchShare.d.ts.map +1 -0
- package/dist/esm/BranchShare.js +213 -0
- package/dist/esm/BranchShare.js.map +1 -0
- package/dist/esm/RunCatalog.d.ts +207 -0
- package/dist/esm/RunCatalog.d.ts.map +1 -0
- package/dist/esm/RunCatalog.js +196 -0
- package/dist/esm/RunCatalog.js.map +1 -0
- package/dist/esm/SyncAuth.d.ts +70 -0
- package/dist/esm/SyncAuth.d.ts.map +1 -0
- package/dist/esm/SyncAuth.js +119 -0
- package/dist/esm/SyncAuth.js.map +1 -0
- package/dist/esm/SyncClient.d.ts +274 -0
- package/dist/esm/SyncClient.d.ts.map +1 -0
- package/dist/esm/SyncClient.js +586 -0
- package/dist/esm/SyncClient.js.map +1 -0
- package/dist/esm/SyncError.d.ts +107 -0
- package/dist/esm/SyncError.d.ts.map +1 -0
- package/dist/esm/SyncError.js +133 -0
- package/dist/esm/SyncError.js.map +1 -0
- package/dist/esm/SyncPrincipal.d.ts +100 -0
- package/dist/esm/SyncPrincipal.d.ts.map +1 -0
- package/dist/esm/SyncPrincipal.js +58 -0
- package/dist/esm/SyncPrincipal.js.map +1 -0
- package/dist/esm/SyncProtocol.d.ts +592 -0
- package/dist/esm/SyncProtocol.d.ts.map +1 -0
- package/dist/esm/SyncProtocol.js +434 -0
- package/dist/esm/SyncProtocol.js.map +1 -0
- package/dist/esm/SyncRpcs.d.ts +92 -0
- package/dist/esm/SyncRpcs.d.ts.map +1 -0
- package/dist/esm/SyncRpcs.js +35 -0
- package/dist/esm/SyncRpcs.js.map +1 -0
- package/dist/esm/SyncServer.d.ts +184 -0
- package/dist/esm/SyncServer.d.ts.map +1 -0
- package/dist/esm/SyncServer.js +897 -0
- package/dist/esm/SyncServer.js.map +1 -0
- package/dist/esm/WorkspaceShare.d.ts +174 -0
- package/dist/esm/WorkspaceShare.d.ts.map +1 -0
- package/dist/esm/WorkspaceShare.js +208 -0
- package/dist/esm/WorkspaceShare.js.map +1 -0
- package/dist/esm/index.d.ts +91 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +91 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/Admission.d.ts +43 -0
- package/dist/esm/internal/Admission.d.ts.map +1 -0
- package/dist/esm/internal/Admission.js +80 -0
- package/dist/esm/internal/Admission.js.map +1 -0
- package/dist/esm/internal/CauseText.d.ts +55 -0
- package/dist/esm/internal/CauseText.d.ts.map +1 -0
- package/dist/esm/internal/CauseText.js +67 -0
- package/dist/esm/internal/CauseText.js.map +1 -0
- package/dist/esm/internal/JournalErrorCode.d.ts +23 -0
- package/dist/esm/internal/JournalErrorCode.d.ts.map +1 -0
- package/dist/esm/internal/JournalErrorCode.js +44 -0
- package/dist/esm/internal/JournalErrorCode.js.map +1 -0
- package/dist/esm/internal/PolicyOptions.d.ts +57 -0
- package/dist/esm/internal/PolicyOptions.d.ts.map +1 -0
- package/dist/esm/internal/PolicyOptions.js +70 -0
- package/dist/esm/internal/PolicyOptions.js.map +1 -0
- package/dist/esm/internal/ProtocolVersion.d.ts +17 -0
- package/dist/esm/internal/ProtocolVersion.d.ts.map +1 -0
- package/dist/esm/internal/ProtocolVersion.js +24 -0
- package/dist/esm/internal/ProtocolVersion.js.map +1 -0
- package/dist/esm/internal/ShareSigner.d.ts +173 -0
- package/dist/esm/internal/ShareSigner.d.ts.map +1 -0
- package/dist/esm/internal/ShareSigner.js +219 -0
- package/dist/esm/internal/ShareSigner.js.map +1 -0
- package/dist/esm/internal/SnapshotBoundary.d.ts +17 -0
- package/dist/esm/internal/SnapshotBoundary.d.ts.map +1 -0
- package/dist/esm/internal/SnapshotBoundary.js +68 -0
- package/dist/esm/internal/SnapshotBoundary.js.map +1 -0
- package/dist/esm/test/TestSocket.d.ts +55 -0
- package/dist/esm/test/TestSocket.d.ts.map +1 -0
- package/dist/esm/test/TestSocket.js +128 -0
- package/dist/esm/test/TestSocket.js.map +1 -0
- package/dist/esm/test/TestSync.d.ts +54 -0
- package/dist/esm/test/TestSync.d.ts.map +1 -0
- package/dist/esm/test/TestSync.js +76 -0
- package/dist/esm/test/TestSync.js.map +1 -0
- package/docs/README.md +156 -0
- package/docs/api.md +328 -0
- package/docs/concepts/authorization.md +117 -0
- package/docs/concepts/branches.md +195 -0
- package/docs/concepts/compaction.md +144 -0
- package/docs/concepts/replay-then-follow.md +129 -0
- package/docs/concepts/scopes-and-cursors.md +135 -0
- package/docs/guides/authorize-a-connection.md +174 -0
- package/docs/guides/follow-a-run.md +174 -0
- package/docs/guides/handle-a-compacted-run.md +158 -0
- package/docs/guides/list-workspace-runs.md +120 -0
- package/docs/guides/run-a-long-soak.md +80 -0
- package/docs/guides/serve-the-read-path.md +146 -0
- package/docs/guides/test-a-follower.md +273 -0
- package/docs/installation.md +92 -0
- package/docs/protocol.md +183 -0
- package/docs/quickstart.md +159 -0
- package/docs/troubleshooting.md +234 -0
- package/package.json +304 -3
- package/src/BranchCommands.ts +684 -0
- package/src/BranchIds.ts +76 -0
- package/src/BranchPresence.ts +512 -0
- package/src/BranchProjection.ts +308 -0
- package/src/BranchProtocol.ts +411 -0
- package/src/BranchRpcs.ts +163 -0
- package/src/BranchServer.ts +202 -0
- package/src/BranchShare.ts +297 -0
- package/src/RunCatalog.ts +308 -0
- package/src/SyncAuth.ts +146 -0
- package/src/SyncClient.ts +902 -0
- package/src/SyncError.ts +147 -0
- package/src/SyncPrincipal.ts +115 -0
- package/src/SyncProtocol.ts +594 -0
- package/src/SyncRpcs.ts +40 -0
- package/src/SyncServer.ts +1184 -0
- package/src/WorkspaceShare.ts +292 -0
- package/src/index.ts +107 -0
- package/src/internal/Admission.ts +119 -0
- package/src/internal/CauseText.ts +74 -0
- package/src/internal/JournalErrorCode.ts +46 -0
- package/src/internal/PolicyOptions.ts +97 -0
- package/src/internal/ProtocolVersion.ts +28 -0
- package/src/internal/ShareSigner.ts +300 -0
- package/src/internal/SnapshotBoundary.ts +91 -0
- package/src/test/TestSocket.ts +192 -0
- package/src/test/TestSync.ts +113 -0
package/src/SyncError.ts
ADDED
|
@@ -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))
|