Split the Projection interface into distinct reader / writer sides.

https://github.com/the-draupnir-project/planning/issues/150
This commit is contained in:
gnuxie committed 2026-09-14 12:36:30 +01:00
1 parent 7822eda0c6
commit 0e3685a0f9
4 files changed
+30 -7

No files matched your search

@@ -17,6 +17,8 @@ import {
ProjectionNode,
} from "./ProjectionNode";
export const ProjectionOrchestrationKey = Symbol("ProjectionOrchestration");
export type ProjectionNodeListener<
TProjectionNode extends ProjectionNode = ProjectionNode,
> = (
@@ -25,10 +27,9 @@ export type ProjectionNodeListener<
previousNode: TProjectionNode
) => void;
export interface Projection<
export interface ProjectionOrchestration<
TProjectionNode extends AnyProjectionNode = AnyProjectionNode,
> {
readonly currentNode: TProjectionNode;
addOutput(projection: Projection): this;
removeOutput(projection: Projection): this;
applyInput(input: ExtractProjectionInputs<TProjectionNode>): void;
@@ -36,6 +37,13 @@ export interface Projection<
removeNodeListener(listener: ProjectionNodeListener<TProjectionNode>): this;
}
export interface Projection<
TProjectionNode extends AnyProjectionNode = AnyProjectionNode,
> {
readonly currentNode: TProjectionNode;
readonly [ProjectionOrchestrationKey]: ProjectionOrchestration<TProjectionNode>;
}
export type ExtractProjectionNode<TProjection> =
TProjection extends Projection<infer TProjectionNode>
? TProjectionNode
@@ -57,7 +65,7 @@ export type ExtractProjectionNode<TProjection> =
export class ProjectionOutputHelper<
TProjectionNode extends ProjectionNode = ProjectionNode,
> {
private readonly outputs = new Set<Projection<ProjectionNode>>();
private readonly outputs = new Set<ProjectionOrchestration<ProjectionNode>>();
private readonly emitter = new EventEmitter();
public constructor(public currentNode: TProjectionNode) {
// nothing to do.
@@ -81,12 +89,12 @@ export class ProjectionOutputHelper<
}
addOutput(projection: Projection): this {
this.outputs.add(projection);
this.outputs.add(projection[ProjectionOrchestrationKey]);
return this;
}
removeOutput(projection: Projection): this {
this.outputs.delete(projection);
this.outputs.delete(projection[ProjectionOrchestrationKey]);
return this;
}
@@ -12,6 +12,7 @@ import { ProtectedRoomsSet } from "./ProtectedRoomsSet";
import { Protection, ProtectionDescription } from "./Protection";
import { AnyProjectionNode } from "../Projection/ProjectionNode";
import { StandardHandleRegistryDescription } from "./HandleRegistry/StandardHandleRegistryDescription";
import { ProjectionOrchestrationKey } from "../Projection/Projection";
export const ProtectionIntentProjectionNodeHandle: HandleDescription<
"handleIntentProjectionNode",
@@ -38,11 +39,15 @@ export const ProtectionIntentProjectionNodeHandle: HandleDescription<
protection.handleIntentProjectionNode?.(node, delta);
return lifetime.allocateResource(
() => {
protection.intentProjection?.addNodeListener(listener);
protection.intentProjection?.[
ProjectionOrchestrationKey
].addNodeListener(listener);
return Ok(listener);
},
() => () => {
protection.intentProjection?.removeNodeListener(listener);
protection.intentProjection?.[
ProjectionOrchestrationKey
].removeNodeListener(listener);
}
) as Result<void>;
},
@@ -14,6 +14,7 @@ import {
} from "../../../MembershipPolicies/SetMembershipPolicyRevisionIssuer";
import {
Projection,
ProjectionOrchestrationKey,
ProjectionOutputHelper,
} from "../../../Projection/Projection";
import {
@@ -44,6 +45,10 @@ export class StandardMemberBanIntentProjection
);
}
public get [ProjectionOrchestrationKey]() {
return this;
}
private handleUpstreamRevision = ((_revision, delta) => {
this.applyInput(delta);
}) satisfies SetMembershipPolicyRevisionListener;
@@ -14,6 +14,7 @@ import {
} from "../../../PolicyList/PolicyListRevisionIssuer";
import {
Projection,
ProjectionOrchestrationKey,
ProjectionOutputHelper,
} from "../../../Projection/Projection";
import {
@@ -49,4 +50,8 @@ export class StandardServerBanIntentProjection
this.policyListRevisionIssuer.off("revision", this.handleUpstreamRevision);
super[Symbol.dispose]();
}
public get [ProjectionOrchestrationKey]() {
return this;
}
}