feat : dont conflit with other editor

This commit is contained in:
2026-09-15 18:51:59 +07:00 Unverified
parent 396a39758b
commit 5c82c956c1
12 changed files with 194 additions and 65 deletions
+20
View File
@@ -41,4 +41,24 @@ describe("invalidation event envelopes", () => {
}),
).toBeNull();
});
it("keeps valid scopes only on authenticated admin events", () => {
const id = "4afeea7b-6f24-43f3-b747-2db98335e01e";
expect(toInvalidationEvent({
topic: "admin",
aggregateId: id,
eventType: "admin.updated",
payload: { id, version: 4, scope: "team" },
})).toEqual({ type: "admin.updated", id, version: 4, scope: "team" });
expect(parseInvalidationEvent(JSON.stringify({ type: "admin.updated", id, version: 5, scope: `extra:${id}` })))
.toEqual({ type: "admin.updated", id, version: 5, scope: `extra:${id}` });
expect(parseInvalidationEvent(JSON.stringify({ type: "page.updated", id, version: 5, scope: "team" })))
.toBeNull();
expect(toInvalidationEvent({
topic: "admin",
aggregateId: id,
eventType: "admin.updated",
payload: { id, version: 6, scope: "unknown" },
})).toBeNull();
});
});
+30 -1
View File
@@ -6,10 +6,20 @@ export const INVALIDATION_TYPES = [
export type InvalidationType = (typeof INVALIDATION_TYPES)[number];
export type AdminEditorScope =
| "overview"
| "weapon"
| "artifact"
| "constellations"
| "team"
| "extras:list"
| `extra:${string}`;
export interface InvalidationEvent {
type: InvalidationType;
id: string;
version: number;
scope?: AdminEditorScope;
}
export interface OutboxEventLike {
@@ -23,6 +33,13 @@ function isInvalidationType(value: string): value is InvalidationType {
return INVALIDATION_TYPES.some((type) => type === value);
}
function isAdminEditorScope(value: unknown): value is AdminEditorScope {
return typeof value === "string" && (
["overview", "weapon", "artifact", "constellations", "team", "extras:list"].includes(value)
|| /^extra:[0-9a-f-]{36}$/iu.test(value)
);
}
export function expectedTopic(event: InvalidationEvent): string {
switch (event.type) {
case "directory.updated":
@@ -38,6 +55,11 @@ export function toInvalidationEvent(
record: OutboxEventLike,
): InvalidationEvent | null {
if (!isInvalidationType(record.eventType)) return null;
if (
record.eventType === "admin.updated"
&& record.payload.scope !== undefined
&& !isAdminEditorScope(record.payload.scope)
) return null;
const id = record.payload.id;
const version = record.payload.version;
if (
@@ -53,6 +75,9 @@ export function toInvalidationEvent(
type: record.eventType,
id,
version,
...(record.eventType === "admin.updated" && isAdminEditorScope(record.payload.scope)
? { scope: record.payload.scope }
: {}),
};
return expectedTopic(event) === record.topic ? event : null;
}
@@ -63,6 +88,7 @@ export function parseInvalidationEvent(value: string): InvalidationEvent | null
const type = parsed.type;
const id = parsed.id;
const version = parsed.version;
const scope = parsed.scope;
if (
typeof type !== "string" ||
!isInvalidationType(type) ||
@@ -73,7 +99,10 @@ export function parseInvalidationEvent(value: string): InvalidationEvent | null
) {
return null;
}
return { type, id, version };
if (scope !== undefined && (type !== "admin.updated" || !isAdminEditorScope(scope))) {
return null;
}
return { type, id, version, ...(scope === undefined ? {} : { scope }) };
} catch {
return null;
}