@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BranchPresence.js","sourceRoot":"","sources":["../../src/BranchPresence.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ephemeral presence and cursors for one shared branch.\n *\n * Presence is deliberately NOT journalled. A roster is a lease table: every\n * announcement extends a lease, and a participant that stops announcing —\n * because the tab closed, the network dropped, or the process died — ages out\n * without anyone reporting the disconnect. Writing presence to the journal\n * would make \"who was looking at this\" part of the durable, replayable history\n * of a run, which is both unbounded and wrong: replaying a branch must not\n * resurrect a stranger's caret.\n *\n * Every operation authorizes through {@link BranchShare}, so a capability for\n * one branch can neither read nor write another branch's roster. A live\n * participant is bound to the capability that announced it: announcing or\n * leaving under that `participantId` with any other capability is refused\n * with `unauthorized` until the lease lapses.\n *\n * @since 0.1.0\n */\n\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as PubSub from \"effect/PubSub\"\nimport * as Schema from \"effect/Schema\"\nimport * as Stream from \"effect/Stream\"\nimport {\n type Announcement,\n type BranchId,\n Cursor,\n type LeaveRequest,\n Participant,\n type ParticipantId,\n PresenceWhere,\n type RosterRequest\n} from \"./BranchProtocol.ts\"\nimport * as BranchShare from \"./BranchShare.ts\"\nimport { positiveInt } from \"./internal/PolicyOptions.ts\"\nimport { SyncError } from \"./SyncError.ts\"\n\n/**\n * Ephemeral branch presence operations.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Service {\n readonly announce: (announcement: Announcement) => Effect.Effect<Participant, SyncError>\n readonly leave: (request: LeaveRequest) => Effect.Effect<void, SyncError>\n /**\n * One branch's live roster, as a fresh array of detached participants and\n * cursors. Reading drops expired leases and advances a cross-branch sweep.\n */\n readonly list: (request: RosterRequest) => Effect.Effect<ReadonlyArray<Participant>, SyncError>\n /**\n * Returns `unknown` for one lease after host startup or while a required\n * heartbeat source is unavailable; otherwise reports `present` or `empty`.\n *\n * @since 1.0.0\n */\n readonly presenceOn: (request: RosterRequest) => Effect.Effect<\"unknown\" | \"present\" | \"empty\", SyncError>\n readonly changes: Stream.Stream<BranchId>\n /**\n * How long one announcement keeps a participant on the roster, in\n * milliseconds.\n *\n * A lease lapses without anyone reporting it, and nothing publishes on\n * `changes` when it does, so a watcher cannot learn of the last\n * participant's departure from the change feed alone. It re-lists on this\n * cadence capped at one second, bounding how long an expired lease stays visible.\n */\n readonly leaseMs: number\n}\n\n/**\n * The branch presence registry.\n *\n * @category services\n * @since 0.1.0\n */\nexport class BranchPresence extends Context.Service<BranchPresence, Service>()(\"@smthrs/sync/BranchPresence\") {}\n\nconst unavailable = new SyncError({ code: \"unsupported\", message: \"Branch presence is unavailable\" })\n\n/**\n * The lease {@link makeNoop} reports. It holds no one, so the value only has\n * to be a positive number a watcher can build a re-list cadence from.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultLeaseMs = 30_000\n\n/**\n * Constructs an unavailable presence registry.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service =>\n BranchPresence.of({\n announce: () => Effect.fail(unavailable),\n leave: () => Effect.fail(unavailable),\n list: () => Effect.fail(unavailable),\n presenceOn: () => Effect.succeed(\"unknown\"),\n changes: Stream.empty,\n leaseMs: defaultLeaseMs,\n ...overrides\n })\n\n/**\n * Provides an unavailable presence registry.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop: Layer.Layer<BranchPresence> = Layer.succeed(BranchPresence, makeNoop())\n\n/**\n * The policy one presence registry runs under. Every field defaults, and\n * {@link makeMemory} and {@link layerWith} validate what a caller supplies.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface PresenceOptions {\n /**\n * How long an announcement keeps a participant on the roster, in\n * milliseconds. Defaults to {@link defaultLeaseMs}.\n */\n readonly leaseMs?: number | undefined\n /**\n * Roster changes a stalled {@link Service.changes} subscriber may fall\n * behind by. Defaults to {@link defaultChangesCapacity}.\n */\n readonly changesCapacity?: number | undefined\n /**\n * Participants one branch may hold at once. Defaults to\n * {@link defaultMaxParticipants}.\n */\n readonly maxParticipants?: number | undefined\n /**\n * Host readiness, including authorization, revocation, bridge and session\n * sources. Defaults to unavailable.\n *\n * @since 1.0.0\n */\n readonly sourcesReady?: ((branchId: BranchId) => Effect.Effect<boolean>) | undefined\n}\n\n/**\n * One roster slot: the participant, and the capability that announced it.\n *\n * `participantId` is chosen by the client, so it proves nothing on its own.\n * The slot is bound to the verified `capabilityId` of its first live\n * announcement, and only that capability may refresh or leave it until the\n * lease lapses. Without the binding any write link on the branch could\n * rename, move the caret of, or evict another link's participant.\n */\ninterface Seat {\n readonly participant: Participant\n readonly capabilityId: string\n}\n\nconst heldByAnother = (participantId: ParticipantId): SyncError =>\n new SyncError({\n code: \"unauthorized\",\n message: `Participant ${participantId} is held by another share capability`\n })\n\n/** The resolved, already-validated presence policy. */\ninterface Resolved {\n readonly leaseMs: number\n readonly changesCapacity: number\n readonly maxParticipants: number\n readonly sourcesReady: (branchId: BranchId) => Effect.Effect<boolean>\n}\n\n/**\n * Roster changes a stalled {@link Service.changes} subscriber may fall behind\n * by before the oldest are dropped.\n *\n * Presence is a lease table, and `changes` only says that some branch's roster\n * moved: a follower answers it with `list`. Holding an unbounded backlog for a\n * subscriber that has stopped pulling would let one abandoned watcher retain\n * every announcement the process has seen since. A subscriber that falls\n * further behind than this bound loses the oldest notifications and re-lists;\n * because every notification is answered by a fresh `list`, dropping one never\n * loses roster state.\n *\n * @category constants\n * @since 0.1.0\n */\nexport const defaultChangesCapacity = 256\n\n/**\n * Participants one branch may hold at once.\n *\n * A roster is unbounded fan-out in two directions: it is walked on every\n * `list`, and `Branch.Roster` returns it whole, which no frame ceiling covers.\n * Nothing caps how many distinct `participantId`s one write capability may\n * announce, so without this a single share link could pin an arbitrary number\n * of `Participant` objects until their leases expired. Two hundred and fifty\n * six is far above any real collaborative session; a further announce is\n * refused with `backpressure`.\n *\n * @category constants\n * @since 1.0.0-rc.0\n */\nexport const defaultMaxParticipants = 256\n\nconst defaults: Resolved = {\n leaseMs: defaultLeaseMs,\n changesCapacity: defaultChangesCapacity,\n maxParticipants: defaultMaxParticipants,\n sourcesReady: () => Effect.succeed(false)\n}\n\n/**\n * Constructs the in-memory, lease-expiring presence registry.\n *\n * Announcing requires write access: a read-only share link may watch the\n * roster but never appears on it, so a shared read link cannot be used to\n * impersonate a collaborator. A live participant belongs to the capability\n * that announced it, so another write link can neither overwrite nor evict it.\n * Holders of one link share its `capabilityId` and stay indistinguishable.\n *\n * The roster is keyed by branch and then by participant, so listing one branch\n * costs that branch plus a bounded sweep of other branches, and no two\n * branches can share a slot. One flat key built by concatenation collided\n * for valid branded ids, which let one announcement overwrite another branch's\n * participant.\n *\n * The change feed slides at {@link defaultChangesCapacity}: announcing never\n * waits on a stalled watcher, and never grows the process on its behalf.\n *\n * Fails with `invalid_request` when an option is not a positive safe integer.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const makeMemory = (\n options: PresenceOptions = {}\n): Effect.Effect<Service, SyncError, BranchShare.BranchShare> =>\n Effect.flatMap(\n Effect.all({\n leaseMs: positiveInt(\"BranchPresence.PresenceOptions.leaseMs\", options.leaseMs, defaults.leaseMs),\n changesCapacity: positiveInt(\n \"BranchPresence.PresenceOptions.changesCapacity\",\n options.changesCapacity,\n defaults.changesCapacity\n ),\n maxParticipants: positiveInt(\n \"BranchPresence.PresenceOptions.maxParticipants\",\n options.maxParticipants,\n defaults.maxParticipants\n )\n }),\n (resolved) => makeResolved({ ...resolved, sourcesReady: options.sourcesReady ?? defaults.sourcesReady })\n )\n\n/** The registry over an already-validated policy. */\nconst makeResolved = (\n { changesCapacity, leaseMs, maxParticipants, sourcesReady }: Resolved\n): Effect.Effect<Service, never, BranchShare.BranchShare> =>\n Effect.gen(function*() {\n const startedAtMs = yield* Clock.currentTimeMillis\n const share = yield* BranchShare.BranchShare\n const roster = new Map<BranchId, Map<string, Seat>>()\n const changes = yield* PubSub.sliding<BranchId>(changesCapacity)\n\n const expire = (branchId: BranchId, nowMs: number) => {\n const branch = roster.get(branchId)\n if (branch === undefined) return undefined\n for (const [participantId, seat] of branch) {\n if (seat.participant.leaseExpiresAtMs <= nowMs) branch.delete(participantId)\n }\n if (branch.size === 0) {\n roster.delete(branchId)\n return undefined\n }\n return branch\n }\n\n // Each announce/list advances through at most 16 branch maps. Their\n // participant counts are capped, so unrelated activity reclaims abandoned\n // rosters without scanning the entire registry in a single request.\n let sweepCursor = roster.keys()\n const sweep = (nowMs: number) => {\n for (let index = 0; index < 16; index++) {\n const next = sweepCursor.next()\n if (next.done) {\n sweepCursor = roster.keys()\n break\n }\n expire(next.value, nowMs)\n }\n }\n\n const seatKey = (request: { participantId: ParticipantId; sessionId?: string }) =>\n JSON.stringify([request.participantId, request.sessionId ?? \"\"])\n const copyWhere = (where: NonNullable<Participant[\"where\"]>) => ({ ...where })\n const detach = (participant: Participant): Participant =>\n new Participant({\n ...participant,\n ...(participant.where === undefined ? {} : { where: copyWhere(participant.where) }),\n cursor: participant.cursor === null ? null : new Cursor(participant.cursor)\n })\n\n /**\n * Drop this branch's expired leases and advance cleanup of other branches.\n * Abandoned maps are reclaimed as announce/list calls advance the sweep;\n * an idle registry keeps them until activity resumes. Results are detached\n * from storage, including each participant's cursor.\n */\n const live = (branchId: BranchId, nowMs: number): Array<Participant> => {\n sweep(nowMs)\n const branch = expire(branchId, nowMs)\n if (branch === undefined) return []\n // Map entries have distinct seat keys, so compared seats cannot be equal.\n return Array.from(branch.values(), (seat) => detach(seat.participant)).sort((left, right) =>\n seatKey(left) < seatKey(right) ? -1 : 1\n )\n }\n\n /**\n * Copy a request out of the caller's object before the first await.\n *\n * `share.verify` awaits Web Crypto between authorizing `branchId` and the\n * roster access that follows. The request objects are plain structs the\n * in-process caller still holds, so reading them again after the await let\n * a caller move `branchId` (or the participant identity) onto a branch the\n * capability never authorized. Matches the claim snapshot in `BranchShare`.\n */\n const detachRoster = (request: RosterRequest): RosterRequest => ({\n capability: request.capability,\n branchId: request.branchId\n })\n const detachLeave = (request: LeaveRequest): LeaveRequest => ({\n ...detachRoster(request),\n participantId: request.participantId,\n ...(request.sessionId === undefined ? {} : { sessionId: request.sessionId })\n })\n const detachAnnouncement = (request: Announcement) => {\n const suppliedCursor: unknown = request.cursor\n const cursor: { readonly cardId: unknown; readonly offset: unknown } | null = suppliedCursor === null\n ? null\n : typeof suppliedCursor === \"object\"\n ? { cardId: (suppliedCursor as Cursor).cardId, offset: (suppliedCursor as Cursor).offset }\n : { cardId: undefined, offset: undefined }\n return {\n ...detachLeave(request),\n displayName: request.displayName,\n cursor,\n ...(request.kind === undefined ? {} : { kind: request.kind }),\n ...(request.where === undefined ? {} : { where: copyWhere(request.where) }),\n ...(request.watching === undefined ? {} : { watching: request.watching }),\n ...(request.for_member === undefined ? {} : { for_member: request.for_member }),\n ...(request.agentKind === undefined ? {} : { agentKind: request.agentKind }),\n ...(request.via === undefined ? {} : { via: request.via }),\n ...(request.runId === undefined ? {} : { runId: request.runId })\n }\n }\n\n const announce = Effect.fn(\"BranchPresence.announce\")(function*(supplied: Announcement) {\n const detached = detachAnnouncement(supplied)\n yield* Effect.annotateCurrentSpan({\n branchId: detached.branchId,\n participantId: detached.participantId\n })\n const claims = yield* share.verify(detached.capability, {\n branchId: detached.branchId,\n access: \"write\"\n })\n // The wire schema IS `Announcement`, so a remote caller cannot reach\n // here with an empty name. An in-process caller can, and `Participant`\n // requires a `NonEmptyString`: without this the constructor threw a\n // defect out of an operation whose type promises a `SyncError`.\n if (detached.displayName.length === 0) {\n return yield* Effect.fail(\n new SyncError({ code: \"invalid_request\", message: \"A participant's display name must not be empty\" })\n )\n }\n if (detached.where !== undefined && !Schema.is(PresenceWhere)(detached.where)) {\n return yield* Effect.fail(new SyncError({ code: \"invalid_request\", message: \"Invalid presence location\" }))\n }\n if (detached.sessionId !== undefined && detached.sessionId.length === 0) {\n return yield* Effect.fail(new SyncError({ code: \"invalid_request\", message: \"Invalid presence session\" }))\n }\n let cursor: Cursor | null = null\n if (detached.cursor !== null) {\n const { cardId, offset } = detached.cursor\n if (typeof cardId !== \"string\" || cardId.length === 0) {\n return yield* Effect.fail(\n new SyncError({ code: \"invalid_request\", message: \"A cursor's card ID must not be empty\" })\n )\n }\n if (typeof offset !== \"number\" || !Number.isSafeInteger(offset) || offset < 0) {\n return yield* Effect.fail(\n new SyncError({ code: \"invalid_request\", message: \"A cursor's offset must be a nonnegative safe integer\" })\n )\n }\n cursor = new Cursor({ cardId, offset })\n }\n const announcement = { ...detached, cursor }\n const nowMs = yield* Clock.currentTimeMillis\n // Run-out leases are dropped before the cap is judged, so a branch that\n // has simply been busy over time is never refused for a stale roster.\n live(announcement.branchId, nowMs)\n const branch = roster.get(announcement.branchId) ?? new Map<string, Seat>()\n const held = branch.get(seatKey(announcement))\n // A session suffix must not bypass the existing participant ownership\n // fence. The host may bind multiple sessions to one actor capability.\n const foreignActor = Array.from(branch.values()).some((seat) =>\n seat.participant.participantId === announcement.participantId && seat.capabilityId !== claims.capabilityId\n )\n if (foreignActor || (held !== undefined && held.capabilityId !== claims.capabilityId)) {\n return yield* Effect.fail(heldByAnother(announcement.participantId))\n }\n if (held === undefined && branch.size >= maxParticipants) {\n return yield* Effect.fail(\n new SyncError({\n code: \"backpressure\",\n message: `Branch ${announcement.branchId} already holds ${maxParticipants} participants`\n })\n )\n }\n const participant = new Participant({\n branchId: announcement.branchId,\n participantId: announcement.participantId,\n displayName: announcement.displayName,\n cursor: announcement.cursor,\n ...(announcement.sessionId === undefined ? {} : { sessionId: announcement.sessionId }),\n ...(announcement.kind === undefined ? {} : { kind: announcement.kind }),\n ...(announcement.where === undefined ? {} : { where: announcement.where }),\n ...(announcement.watching === undefined ? {} : { watching: announcement.watching }),\n ...(announcement.for_member === undefined ? {} : { for_member: announcement.for_member }),\n ...(announcement.agentKind === undefined ? {} : { agentKind: announcement.agentKind }),\n ...(announcement.via === undefined ? {} : { via: announcement.via }),\n ...(announcement.runId === undefined ? {} : { runId: announcement.runId }),\n leaseExpiresAtMs: nowMs + leaseMs\n })\n branch.set(seatKey(announcement), { participant, capabilityId: claims.capabilityId })\n roster.set(announcement.branchId, branch)\n yield* PubSub.publish(changes, announcement.branchId)\n return detach(participant)\n })\n\n const leave = Effect.fn(\"BranchPresence.leave\")(function*(supplied: LeaveRequest) {\n const request = detachLeave(supplied)\n yield* Effect.annotateCurrentSpan({ branchId: request.branchId, participantId: request.participantId })\n const claims = yield* share.verify(request.capability, { branchId: request.branchId, access: \"write\" })\n const branch = expire(request.branchId, yield* Clock.currentTimeMillis)\n if (branch !== undefined) {\n const keys = request.sessionId === undefined\n ? [...branch].filter(([, seat]) => seat.participant.participantId === request.participantId)\n .map(([key]) => key)\n : [seatKey(request)]\n for (const key of keys) {\n const held = branch.get(key)\n if (held !== undefined && held.capabilityId !== claims.capabilityId) {\n return yield* Effect.fail(heldByAnother(request.participantId))\n }\n }\n for (const key of keys) branch.delete(key)\n if (branch.size === 0) roster.delete(request.branchId)\n }\n yield* PubSub.publish(changes, request.branchId)\n })\n\n const list = Effect.fn(\"BranchPresence.list\")(function*(supplied: RosterRequest) {\n const request = detachRoster(supplied)\n yield* Effect.annotateCurrentSpan({ branchId: request.branchId })\n yield* share.verify(request.capability, { branchId: request.branchId, access: \"read\" })\n return live(request.branchId, yield* Clock.currentTimeMillis)\n })\n\n const presenceOn = Effect.fn(\"BranchPresence.presenceOn\")(function*(supplied: RosterRequest) {\n const request = detachRoster(supplied)\n yield* share.verify(request.capability, { branchId: request.branchId, access: \"read\" })\n const nowMs = yield* Clock.currentTimeMillis\n if (nowMs - startedAtMs < leaseMs || !(yield* sourcesReady(request.branchId))) return \"unknown\" as const\n return live(request.branchId, nowMs).length === 0 ? \"empty\" as const : \"present\" as const\n })\n\n return BranchPresence.of({ announce, leave, list, presenceOn, changes: Stream.fromPubSub(changes), leaseMs })\n })\n\n/**\n * Provides the in-memory, lease-expiring presence registry under the default\n * policy, which is valid by construction and so cannot fail.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<BranchPresence, never, BranchShare.BranchShare> = Layer.effect(\n BranchPresence,\n makeResolved(defaults)\n)\n\n/**\n * Provides the in-memory, lease-expiring presence registry under an explicit\n * policy. Fails with `invalid_request` when an option is not a positive safe\n * integer, so a bad policy fails the composition rather than an announcement.\n *\n * @category layers\n * @since 1.0.0-rc.0\n */\nexport const layerWith = (\n options: PresenceOptions\n): Layer.Layer<BranchPresence, SyncError, BranchShare.BranchShare> => Layer.effect(BranchPresence, makeMemory(options))\n"]}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The branch document projection: journal entries folded into what two people
|
|
3
|
+
* looking at the same branch must agree on.
|
|
4
|
+
*
|
|
5
|
+
* The fold is order-independent over the canonical entry set, which is what
|
|
6
|
+
* makes convergence a property rather than a hope: any two clients that have
|
|
7
|
+
* applied the same set of entries hold the same state, regardless of the
|
|
8
|
+
* order their transports delivered frames in or how many times a frame
|
|
9
|
+
* arrived.
|
|
10
|
+
*
|
|
11
|
+
* Three guards do all the work, and each is a production invariant:
|
|
12
|
+
*
|
|
13
|
+
* - entries from another branch's run are ignored, so a mis-routed frame can
|
|
14
|
+
* never leak into a projection;
|
|
15
|
+
* - an entry already folded — the cursor tip redelivered, or a command id
|
|
16
|
+
* already applied — is ignored, so an at-least-once transport cannot
|
|
17
|
+
* double-apply a user action and a reconnect replays no side effects;
|
|
18
|
+
* - an entry below the cursor whose command was never applied is folded into
|
|
19
|
+
* its canonical `seq` position, so out-of-order delivery converges on the
|
|
20
|
+
* same document instead of permanently dropping the late entry.
|
|
21
|
+
*
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
*/
|
|
24
|
+
import * as JournalEvent from "@smthrs/journal/JournalEvent";
|
|
25
|
+
import * as Schema from "effect/Schema";
|
|
26
|
+
import { BranchId } from "./BranchProtocol.ts";
|
|
27
|
+
/**
|
|
28
|
+
* One collaborative chat message in the branch timeline.
|
|
29
|
+
*
|
|
30
|
+
* @category models
|
|
31
|
+
* @since 0.1.0
|
|
32
|
+
*/
|
|
33
|
+
export declare const Message: Schema.Struct<{
|
|
34
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
35
|
+
readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
|
|
36
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
37
|
+
/** The verified share capability that submitted the message. */
|
|
38
|
+
readonly capabilityId: Schema.String;
|
|
39
|
+
readonly text: Schema.String;
|
|
40
|
+
}>;
|
|
41
|
+
/**
|
|
42
|
+
* The value form of {@link Message}.
|
|
43
|
+
*
|
|
44
|
+
* @category models
|
|
45
|
+
* @since 0.1.0
|
|
46
|
+
*/
|
|
47
|
+
export type Message = typeof Message.Type;
|
|
48
|
+
/**
|
|
49
|
+
* One command applied to the branch document.
|
|
50
|
+
*
|
|
51
|
+
* @category models
|
|
52
|
+
* @since 0.1.0
|
|
53
|
+
*/
|
|
54
|
+
export declare const AppliedCommand: Schema.Struct<{
|
|
55
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
56
|
+
readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
|
|
57
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
58
|
+
/** The verified share capability that submitted the command. */
|
|
59
|
+
readonly capabilityId: Schema.String;
|
|
60
|
+
readonly name: Schema.NonEmptyString;
|
|
61
|
+
readonly args: Schema.String;
|
|
62
|
+
readonly target: Schema.String;
|
|
63
|
+
}>;
|
|
64
|
+
/**
|
|
65
|
+
* The value form of {@link AppliedCommand}.
|
|
66
|
+
*
|
|
67
|
+
* @category models
|
|
68
|
+
* @since 0.1.0
|
|
69
|
+
*/
|
|
70
|
+
export type AppliedCommand = typeof AppliedCommand.Type;
|
|
71
|
+
/**
|
|
72
|
+
* The current winning value of one durably edited shared field.
|
|
73
|
+
*
|
|
74
|
+
* @category models
|
|
75
|
+
* @since 0.1.0
|
|
76
|
+
*/
|
|
77
|
+
export declare const Field: Schema.Struct<{
|
|
78
|
+
readonly target: Schema.NonEmptyString;
|
|
79
|
+
readonly value: Schema.String;
|
|
80
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
81
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
82
|
+
/** The verified share capability that wrote the winning value. */
|
|
83
|
+
readonly capabilityId: Schema.String;
|
|
84
|
+
}>;
|
|
85
|
+
/**
|
|
86
|
+
* The value form of {@link Field}.
|
|
87
|
+
*
|
|
88
|
+
* @category models
|
|
89
|
+
* @since 0.1.0
|
|
90
|
+
*/
|
|
91
|
+
export type Field = typeof Field.Type;
|
|
92
|
+
/**
|
|
93
|
+
* The projected state of one branch document.
|
|
94
|
+
*
|
|
95
|
+
* `seq` is the highest journal sequence folded in and is exactly the cursor a
|
|
96
|
+
* client resumes from.
|
|
97
|
+
*
|
|
98
|
+
* @category models
|
|
99
|
+
* @since 0.1.0
|
|
100
|
+
*/
|
|
101
|
+
export declare const State: Schema.Struct<{
|
|
102
|
+
readonly branchId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/BranchId">;
|
|
103
|
+
readonly seq: Schema.Int;
|
|
104
|
+
readonly messages: Schema.$Array<Schema.Struct<{
|
|
105
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
106
|
+
readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
|
|
107
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
108
|
+
/** The verified share capability that submitted the message. */
|
|
109
|
+
readonly capabilityId: Schema.String;
|
|
110
|
+
readonly text: Schema.String;
|
|
111
|
+
}>>;
|
|
112
|
+
readonly commands: Schema.$Array<Schema.Struct<{
|
|
113
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
114
|
+
readonly commandId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/CommandId">;
|
|
115
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
116
|
+
/** The verified share capability that submitted the command. */
|
|
117
|
+
readonly capabilityId: Schema.String;
|
|
118
|
+
readonly name: Schema.NonEmptyString;
|
|
119
|
+
readonly args: Schema.String;
|
|
120
|
+
readonly target: Schema.String;
|
|
121
|
+
}>>;
|
|
122
|
+
readonly fields: Schema.$Array<Schema.Struct<{
|
|
123
|
+
readonly target: Schema.NonEmptyString;
|
|
124
|
+
readonly value: Schema.String;
|
|
125
|
+
readonly seq: Schema.brand<Schema.Int, "@smthrs/journal/JournalEvent/Seq">;
|
|
126
|
+
readonly participantId: Schema.brand<Schema.NonEmptyString, "@smthrs/sync/BranchProtocol/ParticipantId">;
|
|
127
|
+
/** The verified share capability that wrote the winning value. */
|
|
128
|
+
readonly capabilityId: Schema.String;
|
|
129
|
+
}>>;
|
|
130
|
+
}>;
|
|
131
|
+
/**
|
|
132
|
+
* The value form of {@link State}.
|
|
133
|
+
*
|
|
134
|
+
* @category models
|
|
135
|
+
* @since 0.1.0
|
|
136
|
+
*/
|
|
137
|
+
export type State = typeof State.Type;
|
|
138
|
+
/**
|
|
139
|
+
* The empty projection of a branch, whose cursor is "nothing applied".
|
|
140
|
+
*
|
|
141
|
+
* @category constructors
|
|
142
|
+
* @since 0.1.0
|
|
143
|
+
*/
|
|
144
|
+
export declare const empty: (branchId: BranchId) => State;
|
|
145
|
+
/**
|
|
146
|
+
* The conflict policy for two durable edits of the same field.
|
|
147
|
+
*
|
|
148
|
+
* Highest canonical sequence wins. Within a branch the sequence is a total
|
|
149
|
+
* order assigned by the journal, so this is already decisive; the
|
|
150
|
+
* lexicographic `participantId` tie-break exists so the policy stays total if
|
|
151
|
+
* a future deployment ever merges two sequence domains. It is deliberately not
|
|
152
|
+
* wall-clock based — client clocks disagree, and a merge rule that depends on
|
|
153
|
+
* them is not deterministic.
|
|
154
|
+
*
|
|
155
|
+
* @category combinators
|
|
156
|
+
* @since 0.1.0
|
|
157
|
+
*/
|
|
158
|
+
export declare const resolveField: (existing: Field | undefined, candidate: Field) => Field;
|
|
159
|
+
/**
|
|
160
|
+
* Folds one journal entry into a branch projection.
|
|
161
|
+
*
|
|
162
|
+
* Redelivery and out-of-order delivery are told apart by identity, not by
|
|
163
|
+
* position: the cursor tip redelivered and a command id already applied are
|
|
164
|
+
* no-ops, while a below-cursor entry never applied is folded into canonical
|
|
165
|
+
* order. Non-command entries only raise the cursor, so re-applying one is
|
|
166
|
+
* idempotent by construction.
|
|
167
|
+
*
|
|
168
|
+
* @category combinators
|
|
169
|
+
* @since 0.1.0
|
|
170
|
+
*/
|
|
171
|
+
export declare const apply: (state: State, entry: JournalEvent.Entry) => State;
|
|
172
|
+
/**
|
|
173
|
+
* Folds a whole entry stream into a branch projection.
|
|
174
|
+
*
|
|
175
|
+
* @category combinators
|
|
176
|
+
* @since 0.1.0
|
|
177
|
+
*/
|
|
178
|
+
export declare const project: (branchId: BranchId, entries: Iterable<JournalEvent.Entry>) => State;
|
|
179
|
+
//# sourceMappingURL=BranchProjection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BranchProjection.d.ts","sourceRoot":"","sources":["../../src/BranchProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAA;AAG5D,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EACL,QAAQ,EAOT,MAAM,qBAAqB,CAAA;AAE5B;;;;;GAKG;AACH,eAAO,MAAM,OAAO;;;;IAIlB,gEAAgE;;;EAGhE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;IAIzB,gEAAgE;;;;;EAKhE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA;AAEvD;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;IAKhB,kEAAkE;;EAElE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAErC;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK;;;;;;;QAnEhB,gEAAgE;;;;;;;;QAsBhE,gEAAgE;;;;;;;;;;;QAyBhE,kEAAkE;;;EA0BlE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AAErC;;;;;GAKG;AACH,eAAO,MAAM,KAAK,GAAI,UAAU,QAAQ,KAAG,KAMzC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,UAAU,KAAK,GAAG,SAAS,EAAE,WAAW,KAAK,KAAG,KAI5E,CAAA;AAwCD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,KAAK,EAAE,OAAO,YAAY,CAAC,KAAK,KAAG,KAuC/D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,UAAU,QAAQ,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,KAAG,KA+CnF,CAAA"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The branch document projection: journal entries folded into what two people
|
|
3
|
+
* looking at the same branch must agree on.
|
|
4
|
+
*
|
|
5
|
+
* The fold is order-independent over the canonical entry set, which is what
|
|
6
|
+
* makes convergence a property rather than a hope: any two clients that have
|
|
7
|
+
* applied the same set of entries hold the same state, regardless of the
|
|
8
|
+
* order their transports delivered frames in or how many times a frame
|
|
9
|
+
* arrived.
|
|
10
|
+
*
|
|
11
|
+
* Three guards do all the work, and each is a production invariant:
|
|
12
|
+
*
|
|
13
|
+
* - entries from another branch's run are ignored, so a mis-routed frame can
|
|
14
|
+
* never leak into a projection;
|
|
15
|
+
* - an entry already folded — the cursor tip redelivered, or a command id
|
|
16
|
+
* already applied — is ignored, so an at-least-once transport cannot
|
|
17
|
+
* double-apply a user action and a reconnect replays no side effects;
|
|
18
|
+
* - an entry below the cursor whose command was never applied is folded into
|
|
19
|
+
* its canonical `seq` position, so out-of-order delivery converges on the
|
|
20
|
+
* same document instead of permanently dropping the late entry.
|
|
21
|
+
*
|
|
22
|
+
* @since 0.1.0
|
|
23
|
+
*/
|
|
24
|
+
import * as JournalEvent from "@smthrs/journal/JournalEvent";
|
|
25
|
+
import * as HashSet from "effect/HashSet";
|
|
26
|
+
import * as Option from "effect/Option";
|
|
27
|
+
import * as Schema from "effect/Schema";
|
|
28
|
+
import { BranchId, branchRunId, CommandEvent, CommandEventPayload, CommandId, ParticipantId, SayCommand } from "./BranchProtocol.js";
|
|
29
|
+
/**
|
|
30
|
+
* One collaborative chat message in the branch timeline.
|
|
31
|
+
*
|
|
32
|
+
* @category models
|
|
33
|
+
* @since 0.1.0
|
|
34
|
+
*/
|
|
35
|
+
export const Message = Schema.Struct({
|
|
36
|
+
seq: JournalEvent.Seq,
|
|
37
|
+
commandId: CommandId,
|
|
38
|
+
participantId: ParticipantId,
|
|
39
|
+
/** The verified share capability that submitted the message. */
|
|
40
|
+
capabilityId: Schema.String,
|
|
41
|
+
text: Schema.String
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* One command applied to the branch document.
|
|
45
|
+
*
|
|
46
|
+
* @category models
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
*/
|
|
49
|
+
export const AppliedCommand = Schema.Struct({
|
|
50
|
+
seq: JournalEvent.Seq,
|
|
51
|
+
commandId: CommandId,
|
|
52
|
+
participantId: ParticipantId,
|
|
53
|
+
/** The verified share capability that submitted the command. */
|
|
54
|
+
capabilityId: Schema.String,
|
|
55
|
+
name: Schema.NonEmptyString,
|
|
56
|
+
args: Schema.String,
|
|
57
|
+
target: Schema.String
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* The current winning value of one durably edited shared field.
|
|
61
|
+
*
|
|
62
|
+
* @category models
|
|
63
|
+
* @since 0.1.0
|
|
64
|
+
*/
|
|
65
|
+
export const Field = Schema.Struct({
|
|
66
|
+
target: Schema.NonEmptyString,
|
|
67
|
+
value: Schema.String,
|
|
68
|
+
seq: JournalEvent.Seq,
|
|
69
|
+
participantId: ParticipantId,
|
|
70
|
+
/** The verified share capability that wrote the winning value. */
|
|
71
|
+
capabilityId: Schema.String
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* The projected state of one branch document.
|
|
75
|
+
*
|
|
76
|
+
* `seq` is the highest journal sequence folded in and is exactly the cursor a
|
|
77
|
+
* client resumes from.
|
|
78
|
+
*
|
|
79
|
+
* @category models
|
|
80
|
+
* @since 0.1.0
|
|
81
|
+
*/
|
|
82
|
+
export const State = Schema.Struct({
|
|
83
|
+
branchId: BranchId,
|
|
84
|
+
seq: Schema.Int.check(Schema.isGreaterThanOrEqualTo(-1)),
|
|
85
|
+
messages: Schema.Array(Message),
|
|
86
|
+
commands: Schema.Array(AppliedCommand),
|
|
87
|
+
fields: Schema.Array(Field)
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* The empty projection of a branch, whose cursor is "nothing applied".
|
|
91
|
+
*
|
|
92
|
+
* @category constructors
|
|
93
|
+
* @since 0.1.0
|
|
94
|
+
*/
|
|
95
|
+
export const empty = (branchId) => ({
|
|
96
|
+
branchId,
|
|
97
|
+
seq: -1,
|
|
98
|
+
messages: [],
|
|
99
|
+
commands: [],
|
|
100
|
+
fields: []
|
|
101
|
+
});
|
|
102
|
+
/**
|
|
103
|
+
* The conflict policy for two durable edits of the same field.
|
|
104
|
+
*
|
|
105
|
+
* Highest canonical sequence wins. Within a branch the sequence is a total
|
|
106
|
+
* order assigned by the journal, so this is already decisive; the
|
|
107
|
+
* lexicographic `participantId` tie-break exists so the policy stays total if
|
|
108
|
+
* a future deployment ever merges two sequence domains. It is deliberately not
|
|
109
|
+
* wall-clock based — client clocks disagree, and a merge rule that depends on
|
|
110
|
+
* them is not deterministic.
|
|
111
|
+
*
|
|
112
|
+
* @category combinators
|
|
113
|
+
* @since 0.1.0
|
|
114
|
+
*/
|
|
115
|
+
export const resolveField = (existing, candidate) => {
|
|
116
|
+
if (existing === undefined)
|
|
117
|
+
return candidate;
|
|
118
|
+
if (candidate.seq !== existing.seq)
|
|
119
|
+
return candidate.seq > existing.seq ? candidate : existing;
|
|
120
|
+
return candidate.participantId > existing.participantId ? candidate : existing;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Inserts one item into a sequence-ordered array at its canonical position.
|
|
124
|
+
*
|
|
125
|
+
* Canonical sequences are unique within a run, so an equal-sequence collision
|
|
126
|
+
* is unrepresentable here: dedup happens before insertion.
|
|
127
|
+
*/
|
|
128
|
+
const insertBySeq = (items, item) => {
|
|
129
|
+
const last = items[items.length - 1];
|
|
130
|
+
if (last === undefined || last.seq <= item.seq)
|
|
131
|
+
return [...items, item];
|
|
132
|
+
let low = 0;
|
|
133
|
+
let high = items.length;
|
|
134
|
+
while (low < high) {
|
|
135
|
+
const middle = Math.floor((low + high) / 2);
|
|
136
|
+
if (items[middle].seq <= item.seq)
|
|
137
|
+
low = middle + 1;
|
|
138
|
+
else
|
|
139
|
+
high = middle;
|
|
140
|
+
}
|
|
141
|
+
const result = items.slice();
|
|
142
|
+
result.splice(low, 0, item);
|
|
143
|
+
return result;
|
|
144
|
+
};
|
|
145
|
+
// Cache by the immutable command array so cursor-only updates share the index.
|
|
146
|
+
// Persistent sets let independent continuations share history without changing
|
|
147
|
+
// an older state's deduplication. Restored states build their index on first use.
|
|
148
|
+
const commandIds = new WeakMap();
|
|
149
|
+
const indexCommands = (commands) => {
|
|
150
|
+
const cached = commandIds.get(commands);
|
|
151
|
+
if (cached !== undefined)
|
|
152
|
+
return cached;
|
|
153
|
+
const index = HashSet.fromIterable(commands.map((command) => command.commandId));
|
|
154
|
+
commandIds.set(commands, index);
|
|
155
|
+
return index;
|
|
156
|
+
};
|
|
157
|
+
const decodeCommand = Schema.decodeUnknownOption(CommandEventPayload);
|
|
158
|
+
/**
|
|
159
|
+
* Folds one journal entry into a branch projection.
|
|
160
|
+
*
|
|
161
|
+
* Redelivery and out-of-order delivery are told apart by identity, not by
|
|
162
|
+
* position: the cursor tip redelivered and a command id already applied are
|
|
163
|
+
* no-ops, while a below-cursor entry never applied is folded into canonical
|
|
164
|
+
* order. Non-command entries only raise the cursor, so re-applying one is
|
|
165
|
+
* idempotent by construction.
|
|
166
|
+
*
|
|
167
|
+
* @category combinators
|
|
168
|
+
* @since 0.1.0
|
|
169
|
+
*/
|
|
170
|
+
export const apply = (state, entry) => {
|
|
171
|
+
if (entry.runId !== branchRunId(state.branchId) || entry.seq === state.seq)
|
|
172
|
+
return state;
|
|
173
|
+
const advanced = entry.seq < state.seq ? state : { ...state, seq: entry.seq };
|
|
174
|
+
if (entry.eventType !== CommandEvent)
|
|
175
|
+
return advanced;
|
|
176
|
+
const decoded = decodeCommand(entry.payload);
|
|
177
|
+
if (Option.isNone(decoded))
|
|
178
|
+
return advanced;
|
|
179
|
+
const commandSubmission = decoded.value;
|
|
180
|
+
const ids = indexCommands(state.commands);
|
|
181
|
+
if (HashSet.has(ids, commandSubmission.commandId))
|
|
182
|
+
return advanced;
|
|
183
|
+
const command = { ...commandSubmission, seq: entry.seq };
|
|
184
|
+
const messages = commandSubmission.name === SayCommand
|
|
185
|
+
? insertBySeq(state.messages, {
|
|
186
|
+
seq: entry.seq,
|
|
187
|
+
commandId: commandSubmission.commandId,
|
|
188
|
+
participantId: commandSubmission.participantId,
|
|
189
|
+
capabilityId: commandSubmission.capabilityId,
|
|
190
|
+
text: commandSubmission.args
|
|
191
|
+
})
|
|
192
|
+
: state.messages;
|
|
193
|
+
const fields = commandSubmission.target === ""
|
|
194
|
+
? state.fields
|
|
195
|
+
: (() => {
|
|
196
|
+
const candidate = {
|
|
197
|
+
target: commandSubmission.target,
|
|
198
|
+
value: commandSubmission.args,
|
|
199
|
+
seq: entry.seq,
|
|
200
|
+
participantId: commandSubmission.participantId,
|
|
201
|
+
capabilityId: commandSubmission.capabilityId
|
|
202
|
+
};
|
|
203
|
+
const winner = resolveField(state.fields.find((field) => field.target === commandSubmission.target), candidate);
|
|
204
|
+
return [...state.fields.filter((field) => field.target !== commandSubmission.target), winner]
|
|
205
|
+
.sort((left, right) => left.target < right.target ? -1 : 1);
|
|
206
|
+
})();
|
|
207
|
+
const commands = insertBySeq(state.commands, command);
|
|
208
|
+
commandIds.set(commands, HashSet.add(ids, command.commandId));
|
|
209
|
+
return { ...advanced, messages, commands, fields };
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* Folds a whole entry stream into a branch projection.
|
|
213
|
+
*
|
|
214
|
+
* @category combinators
|
|
215
|
+
* @since 0.1.0
|
|
216
|
+
*/
|
|
217
|
+
export const project = (branchId, entries) => {
|
|
218
|
+
const runId = branchRunId(branchId);
|
|
219
|
+
let seq = -1;
|
|
220
|
+
const ids = new Set();
|
|
221
|
+
const commands = [];
|
|
222
|
+
const messages = [];
|
|
223
|
+
const fields = new Map();
|
|
224
|
+
for (const entry of entries) {
|
|
225
|
+
if (entry.runId !== runId || entry.seq === seq)
|
|
226
|
+
continue;
|
|
227
|
+
seq = Math.max(seq, entry.seq);
|
|
228
|
+
if (entry.eventType !== CommandEvent)
|
|
229
|
+
continue;
|
|
230
|
+
const decoded = decodeCommand(entry.payload);
|
|
231
|
+
if (Option.isNone(decoded))
|
|
232
|
+
continue;
|
|
233
|
+
const submission = decoded.value;
|
|
234
|
+
if (ids.has(submission.commandId))
|
|
235
|
+
continue;
|
|
236
|
+
ids.add(submission.commandId);
|
|
237
|
+
commands.push({ ...submission, seq: entry.seq });
|
|
238
|
+
if (submission.name === SayCommand) {
|
|
239
|
+
messages.push({
|
|
240
|
+
seq: entry.seq,
|
|
241
|
+
commandId: submission.commandId,
|
|
242
|
+
participantId: submission.participantId,
|
|
243
|
+
capabilityId: submission.capabilityId,
|
|
244
|
+
text: submission.args
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
if (submission.target !== "") {
|
|
248
|
+
const candidate = {
|
|
249
|
+
target: submission.target,
|
|
250
|
+
value: submission.args,
|
|
251
|
+
seq: entry.seq,
|
|
252
|
+
participantId: submission.participantId,
|
|
253
|
+
capabilityId: submission.capabilityId
|
|
254
|
+
};
|
|
255
|
+
fields.set(submission.target, resolveField(fields.get(submission.target), candidate));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
commands.sort((left, right) => left.seq - right.seq);
|
|
259
|
+
messages.sort((left, right) => left.seq - right.seq);
|
|
260
|
+
commandIds.set(commands, HashSet.fromIterable(ids));
|
|
261
|
+
return {
|
|
262
|
+
branchId,
|
|
263
|
+
seq,
|
|
264
|
+
commands,
|
|
265
|
+
messages,
|
|
266
|
+
fields: Array.from(fields.values()).sort((left, right) => left.target < right.target ? -1 : 1)
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
//# sourceMappingURL=BranchProjection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BranchProjection.js","sourceRoot":"","sources":["../../src/BranchProjection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAA;AAC5D,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,SAAS,EACT,aAAa,EACb,UAAU,EACX,MAAM,qBAAqB,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,GAAG,EAAE,YAAY,CAAC,GAAG;IACrB,SAAS,EAAE,SAAS;IACpB,aAAa,EAAE,aAAa;IAC5B,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC,MAAM;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM;CACpB,CAAC,CAAA;AASF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,GAAG,EAAE,YAAY,CAAC,GAAG;IACrB,SAAS,EAAE,SAAS;IACpB,aAAa,EAAE,aAAa;IAC5B,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC,MAAM;IAC3B,IAAI,EAAE,MAAM,CAAC,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC,MAAM;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM;CACtB,CAAC,CAAA;AASF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,MAAM,EAAE,MAAM,CAAC,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM;IACpB,GAAG,EAAE,YAAY,CAAC,GAAG;IACrB,aAAa,EAAE,aAAa;IAC5B,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAC,MAAM;CAC5B,CAAC,CAAA;AASF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;CAC5B,CAAC,CAAA;AASF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,QAAkB,EAAS,EAAE,CAAC,CAAC;IACnD,QAAQ;IACR,GAAG,EAAE,CAAC,CAAC;IACP,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,EAAE;CACX,CAAC,CAAA;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAA2B,EAAE,SAAgB,EAAS,EAAE;IACnF,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAA;IAC5C,IAAI,SAAS,CAAC,GAAG,KAAK,QAAQ,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;IAC9F,OAAO,SAAS,CAAC,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAA;AAChF,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,GAAG,CAClB,KAA0B,EAC1B,IAAU,EACG,EAAE;IACf,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,CAAA;IACvE,IAAI,GAAG,GAAG,CAAC,CAAA;IACX,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,CAAA;IACvB,OAAO,GAAG,GAAG,IAAI,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;QAC3C,IAAI,KAAK,CAAC,MAAM,CAAE,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;YAAE,GAAG,GAAG,MAAM,GAAG,CAAC,CAAA;;YAC/C,IAAI,GAAG,MAAM,CAAA;IACpB,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;IAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;IAC3B,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,kFAAkF;AAClF,MAAM,UAAU,GAAG,IAAI,OAAO,EAA6D,CAAA;AAC3F,MAAM,aAAa,GAAG,CAAC,QAAuC,EAA8B,EAAE;IAC5F,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;IAChF,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC/B,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,aAAa,GAAG,MAAM,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAA;AAErE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,KAAyB,EAAS,EAAE;IACtE,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG;QAAE,OAAO,KAAK,CAAA;IACxF,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;IAC7E,IAAI,KAAK,CAAC,SAAS,KAAK,YAAY;QAAE,OAAO,QAAQ,CAAA;IACrD,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC5C,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;QAAE,OAAO,QAAQ,CAAA;IAC3C,MAAM,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAA;IACvC,MAAM,GAAG,GAAG,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IACzC,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,iBAAiB,CAAC,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAA;IAClE,MAAM,OAAO,GAAmB,EAAE,GAAG,iBAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAA;IACxE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,IAAI,KAAK,UAAU;QACpD,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE;YAC5B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,SAAS,EAAE,iBAAiB,CAAC,SAAS;YACtC,aAAa,EAAE,iBAAiB,CAAC,aAAa;YAC9C,YAAY,EAAE,iBAAiB,CAAC,YAAY;YAC5C,IAAI,EAAE,iBAAiB,CAAC,IAAI;SAC7B,CAAC;QACF,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAA;IAClB,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,KAAK,EAAE;QAC5C,CAAC,CAAC,KAAK,CAAC,MAAM;QACd,CAAC,CAAC,CAAC,GAAG,EAAE;YACN,MAAM,SAAS,GAAU;gBACvB,MAAM,EAAE,iBAAiB,CAAC,MAAM;gBAChC,KAAK,EAAE,iBAAiB,CAAC,IAAI;gBAC7B,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,aAAa,EAAE,iBAAiB,CAAC,aAAa;gBAC9C,YAAY,EAAE,iBAAiB,CAAC,YAAY;aAC7C,CAAA;YACD,MAAM,MAAM,GAAG,YAAY,CACzB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM,CAAC,EACvE,SAAS,CACV,CAAA;YACD,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,KAAK,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;iBAC1F,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/D,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACrD,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAA;IAC7D,OAAO,EAAE,GAAG,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAA;AACpD,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,QAAkB,EAAE,OAAqC,EAAS,EAAE;IAC1F,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;IACnC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAA;IACZ,MAAM,GAAG,GAAG,IAAI,GAAG,EAAa,CAAA;IAChC,MAAM,QAAQ,GAA0B,EAAE,CAAA;IAC1C,MAAM,QAAQ,GAAmB,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAA;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG;YAAE,SAAQ;QACxD,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;QAC9B,IAAI,KAAK,CAAC,SAAS,KAAK,YAAY;YAAE,SAAQ;QAC9C,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC5C,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,SAAQ;QACpC,MAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,IAAI,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAQ;QAC3C,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAA;QAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;QAChD,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC;gBACZ,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,SAAS,EAAE,UAAU,CAAC,SAAS;gBAC/B,aAAa,EAAE,UAAU,CAAC,aAAa;gBACvC,YAAY,EAAE,UAAU,CAAC,YAAY;gBACrC,IAAI,EAAE,UAAU,CAAC,IAAI;aACtB,CAAC,CAAA;QACJ,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAU;gBACvB,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,KAAK,EAAE,UAAU,CAAC,IAAI;gBACtB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,aAAa,EAAE,UAAU,CAAC,aAAa;gBACvC,YAAY,EAAE,UAAU,CAAC,YAAY;aACtC,CAAA;YACD,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAA;QACvF,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IACpD,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;IACpD,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAA;IACnD,OAAO;QACL,QAAQ;QACR,GAAG;QACH,QAAQ;QACR,QAAQ;QACR,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC/F,CAAA;AACH,CAAC,CAAA","sourcesContent":["/**\n * The branch document projection: journal entries folded into what two people\n * looking at the same branch must agree on.\n *\n * The fold is order-independent over the canonical entry set, which is what\n * makes convergence a property rather than a hope: any two clients that have\n * applied the same set of entries hold the same state, regardless of the\n * order their transports delivered frames in or how many times a frame\n * arrived.\n *\n * Three guards do all the work, and each is a production invariant:\n *\n * - entries from another branch's run are ignored, so a mis-routed frame can\n * never leak into a projection;\n * - an entry already folded — the cursor tip redelivered, or a command id\n * already applied — is ignored, so an at-least-once transport cannot\n * double-apply a user action and a reconnect replays no side effects;\n * - an entry below the cursor whose command was never applied is folded into\n * its canonical `seq` position, so out-of-order delivery converges on the\n * same document instead of permanently dropping the late entry.\n *\n * @since 0.1.0\n */\n\nimport * as JournalEvent from \"@smthrs/journal/JournalEvent\"\nimport * as HashSet from \"effect/HashSet\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport {\n BranchId,\n branchRunId,\n CommandEvent,\n CommandEventPayload,\n CommandId,\n ParticipantId,\n SayCommand\n} from \"./BranchProtocol.ts\"\n\n/**\n * One collaborative chat message in the branch timeline.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Message = Schema.Struct({\n seq: JournalEvent.Seq,\n commandId: CommandId,\n participantId: ParticipantId,\n /** The verified share capability that submitted the message. */\n capabilityId: Schema.String,\n text: Schema.String\n})\n/**\n * The value form of {@link Message}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Message = typeof Message.Type\n\n/**\n * One command applied to the branch document.\n *\n * @category models\n * @since 0.1.0\n */\nexport const AppliedCommand = Schema.Struct({\n seq: JournalEvent.Seq,\n commandId: CommandId,\n participantId: ParticipantId,\n /** The verified share capability that submitted the command. */\n capabilityId: Schema.String,\n name: Schema.NonEmptyString,\n args: Schema.String,\n target: Schema.String\n})\n/**\n * The value form of {@link AppliedCommand}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type AppliedCommand = typeof AppliedCommand.Type\n\n/**\n * The current winning value of one durably edited shared field.\n *\n * @category models\n * @since 0.1.0\n */\nexport const Field = Schema.Struct({\n target: Schema.NonEmptyString,\n value: Schema.String,\n seq: JournalEvent.Seq,\n participantId: ParticipantId,\n /** The verified share capability that wrote the winning value. */\n capabilityId: Schema.String\n})\n/**\n * The value form of {@link Field}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type Field = typeof Field.Type\n\n/**\n * The projected state of one branch document.\n *\n * `seq` is the highest journal sequence folded in and is exactly the cursor a\n * client resumes from.\n *\n * @category models\n * @since 0.1.0\n */\nexport const State = Schema.Struct({\n branchId: BranchId,\n seq: Schema.Int.check(Schema.isGreaterThanOrEqualTo(-1)),\n messages: Schema.Array(Message),\n commands: Schema.Array(AppliedCommand),\n fields: Schema.Array(Field)\n})\n/**\n * The value form of {@link State}.\n *\n * @category models\n * @since 0.1.0\n */\nexport type State = typeof State.Type\n\n/**\n * The empty projection of a branch, whose cursor is \"nothing applied\".\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const empty = (branchId: BranchId): State => ({\n branchId,\n seq: -1,\n messages: [],\n commands: [],\n fields: []\n})\n\n/**\n * The conflict policy for two durable edits of the same field.\n *\n * Highest canonical sequence wins. Within a branch the sequence is a total\n * order assigned by the journal, so this is already decisive; the\n * lexicographic `participantId` tie-break exists so the policy stays total if\n * a future deployment ever merges two sequence domains. It is deliberately not\n * wall-clock based — client clocks disagree, and a merge rule that depends on\n * them is not deterministic.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const resolveField = (existing: Field | undefined, candidate: Field): Field => {\n if (existing === undefined) return candidate\n if (candidate.seq !== existing.seq) return candidate.seq > existing.seq ? candidate : existing\n return candidate.participantId > existing.participantId ? candidate : existing\n}\n\n/**\n * Inserts one item into a sequence-ordered array at its canonical position.\n *\n * Canonical sequences are unique within a run, so an equal-sequence collision\n * is unrepresentable here: dedup happens before insertion.\n */\nconst insertBySeq = <Item extends { readonly seq: JournalEvent.Seq }>(\n items: ReadonlyArray<Item>,\n item: Item\n): Array<Item> => {\n const last = items[items.length - 1]\n if (last === undefined || last.seq <= item.seq) return [...items, item]\n let low = 0\n let high = items.length\n while (low < high) {\n const middle = Math.floor((low + high) / 2)\n if (items[middle]!.seq <= item.seq) low = middle + 1\n else high = middle\n }\n const result = items.slice()\n result.splice(low, 0, item)\n return result\n}\n\n// Cache by the immutable command array so cursor-only updates share the index.\n// Persistent sets let independent continuations share history without changing\n// an older state's deduplication. Restored states build their index on first use.\nconst commandIds = new WeakMap<ReadonlyArray<AppliedCommand>, HashSet.HashSet<CommandId>>()\nconst indexCommands = (commands: ReadonlyArray<AppliedCommand>): HashSet.HashSet<CommandId> => {\n const cached = commandIds.get(commands)\n if (cached !== undefined) return cached\n const index = HashSet.fromIterable(commands.map((command) => command.commandId))\n commandIds.set(commands, index)\n return index\n}\n\nconst decodeCommand = Schema.decodeUnknownOption(CommandEventPayload)\n\n/**\n * Folds one journal entry into a branch projection.\n *\n * Redelivery and out-of-order delivery are told apart by identity, not by\n * position: the cursor tip redelivered and a command id already applied are\n * no-ops, while a below-cursor entry never applied is folded into canonical\n * order. Non-command entries only raise the cursor, so re-applying one is\n * idempotent by construction.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const apply = (state: State, entry: JournalEvent.Entry): State => {\n if (entry.runId !== branchRunId(state.branchId) || entry.seq === state.seq) return state\n const advanced = entry.seq < state.seq ? state : { ...state, seq: entry.seq }\n if (entry.eventType !== CommandEvent) return advanced\n const decoded = decodeCommand(entry.payload)\n if (Option.isNone(decoded)) return advanced\n const commandSubmission = decoded.value\n const ids = indexCommands(state.commands)\n if (HashSet.has(ids, commandSubmission.commandId)) return advanced\n const command: AppliedCommand = { ...commandSubmission, seq: entry.seq }\n const messages = commandSubmission.name === SayCommand\n ? insertBySeq(state.messages, {\n seq: entry.seq,\n commandId: commandSubmission.commandId,\n participantId: commandSubmission.participantId,\n capabilityId: commandSubmission.capabilityId,\n text: commandSubmission.args\n })\n : state.messages\n const fields = commandSubmission.target === \"\"\n ? state.fields\n : (() => {\n const candidate: Field = {\n target: commandSubmission.target,\n value: commandSubmission.args,\n seq: entry.seq,\n participantId: commandSubmission.participantId,\n capabilityId: commandSubmission.capabilityId\n }\n const winner = resolveField(\n state.fields.find((field) => field.target === commandSubmission.target),\n candidate\n )\n return [...state.fields.filter((field) => field.target !== commandSubmission.target), winner]\n .sort((left, right) => left.target < right.target ? -1 : 1)\n })()\n const commands = insertBySeq(state.commands, command)\n commandIds.set(commands, HashSet.add(ids, command.commandId))\n return { ...advanced, messages, commands, fields }\n}\n\n/**\n * Folds a whole entry stream into a branch projection.\n *\n * @category combinators\n * @since 0.1.0\n */\nexport const project = (branchId: BranchId, entries: Iterable<JournalEvent.Entry>): State => {\n const runId = branchRunId(branchId)\n let seq = -1\n const ids = new Set<CommandId>()\n const commands: Array<AppliedCommand> = []\n const messages: Array<Message> = []\n const fields = new Map<string, Field>()\n for (const entry of entries) {\n if (entry.runId !== runId || entry.seq === seq) continue\n seq = Math.max(seq, entry.seq)\n if (entry.eventType !== CommandEvent) continue\n const decoded = decodeCommand(entry.payload)\n if (Option.isNone(decoded)) continue\n const submission = decoded.value\n if (ids.has(submission.commandId)) continue\n ids.add(submission.commandId)\n commands.push({ ...submission, seq: entry.seq })\n if (submission.name === SayCommand) {\n messages.push({\n seq: entry.seq,\n commandId: submission.commandId,\n participantId: submission.participantId,\n capabilityId: submission.capabilityId,\n text: submission.args\n })\n }\n if (submission.target !== \"\") {\n const candidate: Field = {\n target: submission.target,\n value: submission.args,\n seq: entry.seq,\n participantId: submission.participantId,\n capabilityId: submission.capabilityId\n }\n fields.set(submission.target, resolveField(fields.get(submission.target), candidate))\n }\n }\n commands.sort((left, right) => left.seq - right.seq)\n messages.sort((left, right) => left.seq - right.seq)\n commandIds.set(commands, HashSet.fromIterable(ids))\n return {\n branchId,\n seq,\n commands,\n messages,\n fields: Array.from(fields.values()).sort((left, right) => left.target < right.target ? -1 : 1)\n }\n}\n"]}
|