Add a builder for protection descriptions.

https://github.com/the-draupnir-project/planning/issues/152
This commit is contained in:
gnuxie committed 2026-09-14 12:36:30 +01:00
1 parent 15483c3870
commit 5ccd245ad6
3 files changed
+155 -21

No files matched your search

@@ -28,6 +28,121 @@ export interface ProjectionDescription<
export type AnyProjectionDescription = ProjectionDescription;
type ProjectionDescriptionRuntime<
TName extends string,
TPartitionKeys extends ProjectionPartitionKeys,
> = {
readonly name: TName;
readonly partitionKeys: TPartitionKeys;
};
export type ProjectionDescriptionBuilder<
TName extends string,
TPartitionKeys extends ProjectionPartitionKeys,
TInputs extends readonly unknown[] = readonly unknown[],
TDownstreamDeltaShape = unknown,
TAccessMixin = Record<never, never>,
> = {
withInputs<
TNextInputs extends readonly unknown[],
>(): ProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TNextInputs,
TDownstreamDeltaShape,
TAccessMixin
>;
withDownstreamDeltaShape<
TNextDownstreamDeltaShape,
>(): ProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TInputs,
TNextDownstreamDeltaShape,
TAccessMixin
>;
withAccessMixin<TNextAccessMixin>(): ProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TInputs,
TDownstreamDeltaShape,
TNextAccessMixin
>;
build(): ProjectionDescription<
TInputs,
TDownstreamDeltaShape,
TAccessMixin,
TPartitionKeys
> & {
readonly name: TName;
};
};
function makeProjectionDescriptionBuilder<
TName extends string,
TPartitionKeys extends ProjectionPartitionKeys,
TInputs extends readonly unknown[] = readonly unknown[],
TDownstreamDeltaShape = unknown,
TAccessMixin = Record<never, never>,
>(
description: ProjectionDescriptionRuntime<TName, TPartitionKeys>
): ProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TInputs,
TDownstreamDeltaShape,
TAccessMixin
> {
return {
withInputs<TNextInputs extends readonly unknown[]>() {
return makeProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TNextInputs,
TDownstreamDeltaShape,
TAccessMixin
>(description);
},
withDownstreamDeltaShape<TNextDownstreamDeltaShape>() {
return makeProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TInputs,
TNextDownstreamDeltaShape,
TAccessMixin
>(description);
},
withAccessMixin<TNextAccessMixin>() {
return makeProjectionDescriptionBuilder<
TName,
TPartitionKeys,
TInputs,
TDownstreamDeltaShape,
TNextAccessMixin
>(description);
},
build() {
return description;
},
};
}
export function describeProjection<
const TName extends string,
const TPartitionKeys extends ProjectionPartitionKeys,
>({
name,
partitionKeys,
}: {
readonly name: TName;
readonly partitionKeys: TPartitionKeys;
}): ProjectionDescriptionBuilder<TName, TPartitionKeys> {
return makeProjectionDescriptionBuilder<TName, TPartitionKeys>({
name,
partitionKeys,
});
}
export type ExtractProjectionDescriptionInputs<
TDescription extends AnyProjectionDescription,
> =
@@ -12,7 +12,10 @@ import {
ExtractInputDeltaShapes,
ProjectionNode,
} from "../../../Projection/ProjectionNode";
import { ProjectionDescription } from "../../../Projection/ProjectionDescription";
import {
describeProjection,
ProjectionDescription,
} from "../../../Projection/ProjectionDescription";
import {
MemberPolicyMatches,
MembershipPolicyRevision,
@@ -50,6 +53,14 @@ type MemberBanIntentMap = PersistentMap<
List<LiteralPolicyRule | GlobPolicyRule>
>;
export type MemberBanIntentProjectionAccessMixin = {
allMembersWithRules(): MemberPolicyMatches[];
isMemberBanned(member: StringUserID): boolean;
allRulesMatchingMember(
member: StringUserID
): (LiteralPolicyRule | GlobPolicyRule)[];
};
function isPolicyRelevant(policy: LiteralPolicyRule | GlobPolicyRule): boolean {
return (
policy.recommendation === Recommendation.Ban ||
@@ -57,17 +68,17 @@ function isPolicyRelevant(policy: LiteralPolicyRule | GlobPolicyRule): boolean {
);
}
export type MemberBanIntentProjectionDescription = ProjectionDescription<
[MemberBanInputProjectionNode],
MemberBanIntentProjectionDelta,
{
allMembersWithRules(): MemberPolicyMatches[];
isMemberBanned(member: StringUserID): boolean;
allRulesMatchingMember(
member: StringUserID
): (LiteralPolicyRule | GlobPolicyRule)[];
}
>;
export const MemberBanIntentProjectionDescription = describeProjection({
name: "MemberBanIntentProjection",
partitionKeys: [],
})
.withInputs<[MemberBanInputProjectionNode]>()
.withDownstreamDeltaShape<MemberBanIntentProjectionDelta>()
.withAccessMixin<MemberBanIntentProjectionAccessMixin>()
.build();
export type MemberBanIntentProjectionDescription =
typeof MemberBanIntentProjectionDescription;
export type MemberBanIntentProjectionNode =
ProjectionNode<MemberBanIntentProjectionDescription>;
@@ -8,7 +8,7 @@
// </text>
import { StringServerName } from "@the-draupnir-project/matrix-basic-types";
import { ProjectionDescription } from "../../../Projection/ProjectionDescription";
import { describeProjection } from "../../../Projection/ProjectionDescription";
import { ProjectionNode } from "../../../Projection/ProjectionNode";
import { PolicyListBridgeProjectionNode } from "./PolicyListBridgeProjection";
import {
@@ -36,17 +36,25 @@ type ServerBanIntentMap = PersistentMap<
List<LiteralPolicyRule | GlobPolicyRule>
>;
export type ServerBanIntentProjectionAccessMixin = {
deny: StringServerName[];
isServerDenied(serverName: StringServerName): boolean;
};
// is there a way that we can adapt this so that it can possibly be swapped
// to a lazy ban style protection if acls become exhausted.
// not withiout addressing the issues in the member protection tbh.
export type ServerBanIntentProjectionDescription = ProjectionDescription<
[PolicyListBridgeProjectionNode],
ServerBanIntentProjectionDelta,
{
deny: StringServerName[];
isServerDenied(serverName: StringServerName): boolean;
}
>;
export const ServerBanIntentProjectionDescription = describeProjection({
name: "ServerBanIntentProjection",
partitionKeys: [],
})
.withInputs<[PolicyListBridgeProjectionNode]>()
.withDownstreamDeltaShape<ServerBanIntentProjectionDelta>()
.withAccessMixin<ServerBanIntentProjectionAccessMixin>()
.build();
export type ServerBanIntentProjectionDescription =
typeof ServerBanIntentProjectionDescription;
export type ServerBanIntentProjectionNode =
ProjectionNode<ServerBanIntentProjectionDescription>;