@ixo/topic-protocol 0.1.0

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.
Files changed (63) hide show
  1. package/README.md +133 -0
  2. package/dist/schemas/action-receipt.schema.json +18 -0
  3. package/dist/schemas/agent-run.schema.json +18 -0
  4. package/dist/schemas/external-session-binding.schema.json +24 -0
  5. package/dist/schemas/room-policy.schema.json +15 -0
  6. package/dist/schemas/topic-capsule.schema.json +19 -0
  7. package/dist/schemas/topic-index-entry.schema.json +17 -0
  8. package/dist/schemas/topic-message.schema.json +18 -0
  9. package/dist/schemas/topic-operation.schema.json +19 -0
  10. package/dist/schemas/topic-preferences.schema.json +13 -0
  11. package/dist/schemas/topic-record.schema.json +18 -0
  12. package/dist/schemas/topic-relation.schema.json +16 -0
  13. package/dist/schemas/topic-root.schema.json +18 -0
  14. package/dist/schemas/topic-state.schema.json +19 -0
  15. package/dist/src/contracts/schemas.d.ts +569 -0
  16. package/dist/src/contracts/schemas.js +30 -0
  17. package/dist/src/contracts/schemas.js.map +1 -0
  18. package/dist/src/contracts/validator.d.ts +14 -0
  19. package/dist/src/contracts/validator.js +43 -0
  20. package/dist/src/contracts/validator.js.map +1 -0
  21. package/dist/src/errors.d.ts +6 -0
  22. package/dist/src/errors.js +11 -0
  23. package/dist/src/errors.js.map +1 -0
  24. package/dist/src/index.d.ts +11 -0
  25. package/dist/src/index.js +12 -0
  26. package/dist/src/index.js.map +1 -0
  27. package/dist/src/matrix/anchors.d.ts +2 -0
  28. package/dist/src/matrix/anchors.js +55 -0
  29. package/dist/src/matrix/anchors.js.map +1 -0
  30. package/dist/src/matrix/content.d.ts +11 -0
  31. package/dist/src/matrix/content.js +64 -0
  32. package/dist/src/matrix/content.js.map +1 -0
  33. package/dist/src/matrix/events.d.ts +4 -0
  34. package/dist/src/matrix/events.js +94 -0
  35. package/dist/src/matrix/events.js.map +1 -0
  36. package/dist/src/matrix/pending.d.ts +22 -0
  37. package/dist/src/matrix/pending.js +83 -0
  38. package/dist/src/matrix/pending.js.map +1 -0
  39. package/dist/src/matrix/runtime.d.ts +18 -0
  40. package/dist/src/matrix/runtime.js +121 -0
  41. package/dist/src/matrix/runtime.js.map +1 -0
  42. package/dist/src/matrix/types.d.ts +84 -0
  43. package/dist/src/matrix/types.js +5 -0
  44. package/dist/src/matrix/types.js.map +1 -0
  45. package/dist/src/projector/payload.d.ts +27 -0
  46. package/dist/src/projector/payload.js +80 -0
  47. package/dist/src/projector/payload.js.map +1 -0
  48. package/dist/src/projector/prepare.d.ts +3 -0
  49. package/dist/src/projector/prepare.js +47 -0
  50. package/dist/src/projector/prepare.js.map +1 -0
  51. package/dist/src/projector/project.d.ts +2 -0
  52. package/dist/src/projector/project.js +89 -0
  53. package/dist/src/projector/project.js.map +1 -0
  54. package/dist/src/projector/state.d.ts +8 -0
  55. package/dist/src/projector/state.js +89 -0
  56. package/dist/src/projector/state.js.map +1 -0
  57. package/dist/src/projector/types.d.ts +76 -0
  58. package/dist/src/projector/types.js +19 -0
  59. package/dist/src/projector/types.js.map +1 -0
  60. package/dist/src/shared/compare.d.ts +2 -0
  61. package/dist/src/shared/compare.js +14 -0
  62. package/dist/src/shared/compare.js.map +1 -0
  63. package/package.json +61 -0
@@ -0,0 +1,11 @@
1
+ export { CONTRACT_NAMES, type ContractName, } from "./contracts/schemas.js";
2
+ export { assertContract, validateContract, type ContractValidationError, type ContractValidationResult, } from "./contracts/validator.js";
3
+ export { TopicProtocolError, type TopicProtocolErrorCode, } from "./errors.js";
4
+ export { projectTopic } from "./projector/project.js";
5
+ export { selectCanonicalTopicAnchor } from "./matrix/anchors.js";
6
+ export { assertMatrixAnchor, assertMatrixRoomId, assertServerEventId, assertTopicMessageContent, buildTopicMessageContent, buildTopicOperationContent, buildTopicRootContent, createTopicId, } from "./matrix/content.js";
7
+ export { isTopicInfrastructureEvent, parseTopicRootEvent, projectMatrixTopic, } from "./matrix/events.js";
8
+ export { JsonPendingTopicStore, MemoryPendingTopicStore, type TopicKeyValueStore, } from "./matrix/pending.js";
9
+ export { TopicRuntime, type TopicRuntimeOptions } from "./matrix/runtime.js";
10
+ export { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, type CanonicalTopicAnchor, type MatrixEventContent, type MatrixSendRequest, type MatrixTopicProjection, type MatrixTopicProjectionInput, type ParsedTopicRoot, type PendingTopicCreation, type PendingTopicStore, type RejectedTopicEvent, type RejectedTopicEventReason, type TopicAnchorConflict, type TopicCreationInput, type TopicCreationResult, type TopicMatrixEvent, type TopicMatrixTransport, type TopicRootContentOptions, } from "./matrix/types.js";
11
+ export { TOPIC_KINDS, TOPIC_STATUSES, type IgnoredOperation, type IgnoredOperationReason, type MatrixAnchor, type ProjectedTopicRelation, type ProjectionInput, type TopicContext, type TopicDecision, type TopicKind, type TopicOperation, type TopicOperationType, type TopicProjection, type TopicProjectionConflict, type TopicRelationType, type TopicRoot, type TopicStatus, } from "./projector/types.js";
@@ -0,0 +1,12 @@
1
+ export { CONTRACT_NAMES, } from "./contracts/schemas.js";
2
+ export { assertContract, validateContract, } from "./contracts/validator.js";
3
+ export { TopicProtocolError, } from "./errors.js";
4
+ export { projectTopic } from "./projector/project.js";
5
+ export { selectCanonicalTopicAnchor } from "./matrix/anchors.js";
6
+ export { assertMatrixAnchor, assertMatrixRoomId, assertServerEventId, assertTopicMessageContent, buildTopicMessageContent, buildTopicOperationContent, buildTopicRootContent, createTopicId, } from "./matrix/content.js";
7
+ export { isTopicInfrastructureEvent, parseTopicRootEvent, projectMatrixTopic, } from "./matrix/events.js";
8
+ export { JsonPendingTopicStore, MemoryPendingTopicStore, } from "./matrix/pending.js";
9
+ export { TopicRuntime } from "./matrix/runtime.js";
10
+ export { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, } from "./matrix/types.js";
11
+ export { TOPIC_KINDS, TOPIC_STATUSES, } from "./projector/types.js";
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAEf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,cAAc,EACd,gBAAgB,GAGjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,GAEnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,0BAA0B,EAC1B,qBAAqB,EACrB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA4B,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,GAiB3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,cAAc,GAgBf,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { CanonicalTopicAnchor, TopicMatrixEvent } from "./types.js";
2
+ export declare function selectCanonicalTopicAnchor(events: readonly TopicMatrixEvent[], topicId: string): CanonicalTopicAnchor;
@@ -0,0 +1,55 @@
1
+ import { TopicProtocolError } from "../errors.js";
2
+ import { compareText } from "../shared/compare.js";
3
+ import { parseTopicRootEvent } from "./events.js";
4
+ function anchorKey(root) {
5
+ return `${root.anchor.roomId}\u0000${root.anchor.rootEventId}`;
6
+ }
7
+ function previousKey(root) {
8
+ return root.replaces === undefined
9
+ ? undefined
10
+ : `${root.replaces.roomId}\u0000${root.replaces.rootEventId}`;
11
+ }
12
+ function compareRoots(left, right) {
13
+ return compareText(left.anchor.rootEventId, right.anchor.rootEventId)
14
+ || compareText(left.anchor.roomId, right.anchor.roomId);
15
+ }
16
+ function conflictFor(previous, contenders) {
17
+ const [canonical, ...competing] = contenders;
18
+ if (canonical === undefined || competing.length === 0)
19
+ return undefined;
20
+ return {
21
+ previous: previous.anchor,
22
+ canonical: canonical.anchor,
23
+ competing: competing.map(({ anchor }) => anchor),
24
+ };
25
+ }
26
+ export function selectCanonicalTopicAnchor(events, topicId) {
27
+ const roots = events
28
+ .map(parseTopicRootEvent)
29
+ .filter((root) => root?.root.id === topicId);
30
+ const initial = roots.filter((root) => root.replaces === undefined).sort(compareRoots);
31
+ if (initial.length === 0) {
32
+ throw new TopicProtocolError("TOPIC_ROOT_NOT_FOUND", "No initial Topic root was found", { topicId });
33
+ }
34
+ if (initial.length > 1) {
35
+ throw new TopicProtocolError("ANCHOR_CONFLICT", "Multiple initial Topic roots were found", { topicId });
36
+ }
37
+ let current = initial[0];
38
+ const conflicts = [];
39
+ const visited = new Set([anchorKey(current)]);
40
+ let contenders = roots.filter((root) => previousKey(root) === anchorKey(current)).sort(compareRoots);
41
+ while (contenders.length > 0) {
42
+ const conflict = conflictFor(current, contenders);
43
+ if (conflict !== undefined)
44
+ conflicts.push(conflict);
45
+ const next = contenders[0];
46
+ if (visited.has(anchorKey(next))) {
47
+ throw new TopicProtocolError("ANCHOR_CYCLE", "Topic anchor replacement contains a cycle", { topicId });
48
+ }
49
+ current = next;
50
+ visited.add(anchorKey(current));
51
+ contenders = roots.filter((root) => previousKey(root) === anchorKey(current)).sort(compareRoots);
52
+ }
53
+ return { anchor: current.anchor, root: current.root, conflicts };
54
+ }
55
+ //# sourceMappingURL=anchors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchors.js","sourceRoot":"","sources":["../../../src/matrix/anchors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { MatrixAnchor, TopicOperation, TopicRoot } from "../projector/types.js";
2
+ import { MATRIX_ROOM_MESSAGE, TOPIC_OPERATION_EVENT_TYPE, type MatrixEventContent, type TopicRootContentOptions } from "./types.js";
3
+ export declare function createTopicId(): string;
4
+ export declare function assertServerEventId(eventId: string): void;
5
+ export declare function assertMatrixRoomId(roomId: string): void;
6
+ export declare function assertMatrixAnchor(anchor: MatrixAnchor): void;
7
+ export declare function assertTopicMessageContent(message: MatrixEventContent): void;
8
+ export declare function buildTopicRootContent(root: TopicRoot, options?: TopicRootContentOptions): MatrixEventContent;
9
+ export declare function buildTopicMessageContent(anchor: MatrixAnchor, message: MatrixEventContent): MatrixEventContent;
10
+ export declare function buildTopicOperationContent(anchor: MatrixAnchor, operation: TopicOperation): MatrixEventContent;
11
+ export { MATRIX_ROOM_MESSAGE, TOPIC_OPERATION_EVENT_TYPE };
@@ -0,0 +1,64 @@
1
+ import { v7 as uuidv7 } from "uuid";
2
+ import { assertContract } from "../contracts/validator.js";
3
+ import { TopicProtocolError } from "../errors.js";
4
+ import { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, } from "./types.js";
5
+ export function createTopicId() {
6
+ return `ixo:topic:${uuidv7()}`;
7
+ }
8
+ export function assertServerEventId(eventId) {
9
+ if (eventId.startsWith("$") && eventId.length > 1)
10
+ return;
11
+ throw new TopicProtocolError("INVALID_SERVER_EVENT_ID", "Topic anchors require a server-assigned Matrix event ID", { eventId });
12
+ }
13
+ export function assertMatrixRoomId(roomId) {
14
+ if (roomId.startsWith("!") && roomId.length > 1)
15
+ return;
16
+ throw new TopicProtocolError("INVALID_ANCHOR", "Topic anchors require a Matrix room ID", { roomId });
17
+ }
18
+ export function assertMatrixAnchor(anchor) {
19
+ assertMatrixRoomId(anchor.roomId);
20
+ assertServerEventId(anchor.rootEventId);
21
+ }
22
+ export function assertTopicMessageContent(message) {
23
+ if (typeof message.msgtype === "string" && typeof message.body === "string")
24
+ return;
25
+ throw new TopicProtocolError("INVALID_MATRIX_EVENT", "Topic messages require Matrix msgtype and body fields");
26
+ }
27
+ export function buildTopicRootContent(root, options = {}) {
28
+ assertContract("topic-root", root);
29
+ if (options.replaces !== undefined)
30
+ assertMatrixAnchor(options.replaces);
31
+ return {
32
+ msgtype: "m.notice",
33
+ body: `Topic: ${root.title}`,
34
+ [TOPIC_METADATA_KEY]: root,
35
+ ...(options.replaces === undefined
36
+ ? {}
37
+ : { [TOPIC_ANCHOR_KEY]: { replaces: options.replaces } }),
38
+ };
39
+ }
40
+ export function buildTopicMessageContent(anchor, message) {
41
+ assertMatrixAnchor(anchor);
42
+ assertTopicMessageContent(message);
43
+ return {
44
+ ...message,
45
+ "m.relates_to": {
46
+ rel_type: "m.thread",
47
+ event_id: anchor.rootEventId,
48
+ is_falling_back: true,
49
+ },
50
+ };
51
+ }
52
+ export function buildTopicOperationContent(anchor, operation) {
53
+ assertMatrixAnchor(anchor);
54
+ assertContract("topic-operation", operation);
55
+ return {
56
+ operation,
57
+ "m.relates_to": {
58
+ rel_type: "m.reference",
59
+ event_id: anchor.rootEventId,
60
+ },
61
+ };
62
+ }
63
+ export { MATRIX_ROOM_MESSAGE, TOPIC_OPERATION_EVENT_TYPE };
64
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../../src/matrix/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,GAG3B,MAAM,YAAY,CAAC;AAEpB,MAAM,UAAU,aAAa;IAC3B,OAAO,aAAa,MAAM,EAAE,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO;IAC1D,MAAM,IAAI,kBAAkB,CAC1B,yBAAyB,EACzB,yDAAyD,EACzD,EAAE,OAAO,EAAE,CACZ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAc;IAC/C,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO;IACxD,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,EAAE,wCAAwC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;AACvG,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAoB;IACrD,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,mBAAmB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,OAA2B;IACnE,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO;IACpF,MAAM,IAAI,kBAAkB,CAC1B,sBAAsB,EACtB,uDAAuD,CACxD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,IAAe,EACf,UAAmC,EAAE;IAErC,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACnC,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;QAAE,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACzE,OAAO;QACL,OAAO,EAAE,UAAU;QACnB,IAAI,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;QAC5B,CAAC,kBAAkB,CAAC,EAAE,IAAI;QAC1B,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS;YAChC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,MAAoB,EACpB,OAA2B;IAE3B,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO;QACL,GAAG,OAAO;QACV,cAAc,EAAE;YACd,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,MAAM,CAAC,WAAW;YAC5B,eAAe,EAAE,IAAI;SACtB;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,MAAoB,EACpB,SAAyB;IAEzB,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,cAAc,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC;IAC7C,OAAO;QACL,SAAS;QACT,cAAc,EAAE;YACd,QAAQ,EAAE,aAAa;YACvB,QAAQ,EAAE,MAAM,CAAC,WAAW;SAC7B;KACF,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type MatrixTopicProjection, type MatrixTopicProjectionInput, type ParsedTopicRoot, type TopicMatrixEvent } from "./types.js";
2
+ export declare function parseTopicRootEvent(event: TopicMatrixEvent): ParsedTopicRoot | null;
3
+ export declare function isTopicInfrastructureEvent(event: TopicMatrixEvent): boolean;
4
+ export declare function projectMatrixTopic(input: MatrixTopicProjectionInput): MatrixTopicProjection;
@@ -0,0 +1,94 @@
1
+ import { validateContract } from "../contracts/validator.js";
2
+ import { TopicProtocolError } from "../errors.js";
3
+ import { projectTopic } from "../projector/project.js";
4
+ import { MATRIX_ROOM_MESSAGE, TOPIC_ANCHOR_KEY, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, } from "./types.js";
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
+ }
8
+ function parseAnchor(value) {
9
+ if (!isRecord(value) || !isRecord(value.replaces))
10
+ return undefined;
11
+ const roomId = value.replaces.roomId;
12
+ const rootEventId = value.replaces.rootEventId;
13
+ if (typeof roomId !== "string" || !roomId.startsWith("!"))
14
+ return undefined;
15
+ if (typeof rootEventId !== "string" || !rootEventId.startsWith("$"))
16
+ return undefined;
17
+ return { roomId, rootEventId };
18
+ }
19
+ function hasServerAnchor(event) {
20
+ return event.roomId.startsWith("!") && event.roomId.length > 1
21
+ && event.eventId.startsWith("$") && event.eventId.length > 1;
22
+ }
23
+ export function parseTopicRootEvent(event) {
24
+ if (event.type !== MATRIX_ROOM_MESSAGE || event.redacted === true)
25
+ return null;
26
+ if (!hasServerAnchor(event))
27
+ return null;
28
+ if (event.content["m.relates_to"] !== undefined)
29
+ return null;
30
+ const root = event.content[TOPIC_METADATA_KEY];
31
+ if (!validateContract("topic-root", root).ok)
32
+ return null;
33
+ const anchorMetadata = event.content[TOPIC_ANCHOR_KEY];
34
+ const replaces = parseAnchor(anchorMetadata);
35
+ if (anchorMetadata !== undefined && replaces === undefined)
36
+ return null;
37
+ const parsed = {
38
+ event,
39
+ root: root,
40
+ anchor: { roomId: event.roomId, rootEventId: event.eventId },
41
+ };
42
+ return replaces === undefined ? parsed : { ...parsed, replaces };
43
+ }
44
+ export function isTopicInfrastructureEvent(event) {
45
+ if (event.type === TOPIC_OPERATION_EVENT_TYPE)
46
+ return true;
47
+ return parseTopicRootEvent(event) !== null;
48
+ }
49
+ function rejected(eventId, reason) {
50
+ return { rejection: { eventId, reason } };
51
+ }
52
+ function relationRoot(content) {
53
+ const relation = content["m.relates_to"];
54
+ if (!isRecord(relation) || relation.rel_type !== "m.reference")
55
+ return undefined;
56
+ return typeof relation.event_id === "string" ? relation.event_id : undefined;
57
+ }
58
+ function parseOperationEvent(event, rootEventId, authenticateActor) {
59
+ if (event.redacted === true)
60
+ return rejected(event.eventId, "redacted");
61
+ if (event.type !== TOPIC_OPERATION_EVENT_TYPE)
62
+ return rejected(event.eventId, "invalid-content");
63
+ const relation = relationRoot(event.content);
64
+ if (relation === undefined)
65
+ return rejected(event.eventId, "invalid-relation");
66
+ if (relation !== rootEventId)
67
+ return rejected(event.eventId, "wrong-root");
68
+ const operation = event.content.operation;
69
+ if (!validateContract("topic-operation", operation).ok)
70
+ return rejected(event.eventId, "invalid-content");
71
+ const typed = operation;
72
+ if (typed.operationId !== event.eventId)
73
+ return rejected(event.eventId, "invalid-content");
74
+ if (!authenticateActor(typed, event))
75
+ return rejected(event.eventId, "actor-mismatch");
76
+ return { operation: typed };
77
+ }
78
+ export function projectMatrixTopic(input) {
79
+ const parsedRoot = parseTopicRootEvent(input.rootEvent);
80
+ if (parsedRoot === null) {
81
+ throw new TopicProtocolError("INVALID_MATRIX_EVENT", "Topic projection requires a valid relation-free root event");
82
+ }
83
+ const parsed = input.operationEvents.map((event) => parseOperationEvent(event, input.rootEvent.eventId, input.authenticateActor));
84
+ const operations = parsed.flatMap(({ operation }) => operation === undefined ? [] : [operation]);
85
+ const rejectedEvents = parsed.flatMap(({ rejection }) => rejection === undefined ? [] : [rejection]);
86
+ const projection = projectTopic({
87
+ root: parsedRoot.root,
88
+ anchor: parsedRoot.anchor,
89
+ operations,
90
+ authorize: input.authorize,
91
+ });
92
+ return { projection, rejectedEvents };
93
+ }
94
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/matrix/events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { PendingTopicCreation, PendingTopicStore } from "./types.js";
2
+ type Awaitable<T> = T | Promise<T>;
3
+ export interface TopicKeyValueStore {
4
+ getItem(key: string): Awaitable<string | null>;
5
+ setItem(key: string, value: string): Awaitable<void>;
6
+ removeItem(key: string): Awaitable<void>;
7
+ }
8
+ export declare class MemoryPendingTopicStore implements PendingTopicStore {
9
+ #private;
10
+ get(roomId: string, topicId: string): Promise<PendingTopicCreation | null>;
11
+ save(pending: PendingTopicCreation): Promise<void>;
12
+ remove(roomId: string, topicId: string): Promise<void>;
13
+ }
14
+ export declare class JsonPendingTopicStore implements PendingTopicStore {
15
+ private readonly storage;
16
+ constructor(storage: TopicKeyValueStore);
17
+ get(roomId: string, topicId: string): Promise<PendingTopicCreation | null>;
18
+ save(pending: PendingTopicCreation): Promise<void>;
19
+ remove(roomId: string, topicId: string): Promise<void>;
20
+ private read;
21
+ }
22
+ export {};
@@ -0,0 +1,83 @@
1
+ import { TopicProtocolError } from "../errors.js";
2
+ const STORAGE_KEY = "ixo.topic.pending.v1";
3
+ function pendingKey(roomId, topicId) {
4
+ return `${roomId}\u0000${topicId}`;
5
+ }
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ }
9
+ function isPending(value) {
10
+ if (!isRecord(value))
11
+ return false;
12
+ return typeof value.roomId === "string"
13
+ && value.roomId.startsWith("!")
14
+ && typeof value.topicId === "string"
15
+ && value.topicId.startsWith("ixo:topic:")
16
+ && (value.rootEventId === null
17
+ || (typeof value.rootEventId === "string" && value.rootEventId.startsWith("$")))
18
+ && typeof value.createdAt === "string";
19
+ }
20
+ function parsePendingValues(value) {
21
+ if (!isRecord(value))
22
+ return null;
23
+ const parsed = {};
24
+ for (const [key, pending] of Object.entries(value)) {
25
+ if (!isPending(pending))
26
+ return null;
27
+ parsed[key] = pending;
28
+ }
29
+ return parsed;
30
+ }
31
+ export class MemoryPendingTopicStore {
32
+ #pending = new Map();
33
+ get(roomId, topicId) {
34
+ return Promise.resolve(this.#pending.get(pendingKey(roomId, topicId)) ?? null);
35
+ }
36
+ save(pending) {
37
+ this.#pending.set(pendingKey(pending.roomId, pending.topicId), pending);
38
+ return Promise.resolve();
39
+ }
40
+ remove(roomId, topicId) {
41
+ this.#pending.delete(pendingKey(roomId, topicId));
42
+ return Promise.resolve();
43
+ }
44
+ }
45
+ export class JsonPendingTopicStore {
46
+ storage;
47
+ constructor(storage) {
48
+ this.storage = storage;
49
+ }
50
+ async get(roomId, topicId) {
51
+ const values = await this.read();
52
+ return values[pendingKey(roomId, topicId)] ?? null;
53
+ }
54
+ async save(pending) {
55
+ const values = await this.read();
56
+ values[pendingKey(pending.roomId, pending.topicId)] = pending;
57
+ await this.storage.setItem(STORAGE_KEY, JSON.stringify(values));
58
+ }
59
+ async remove(roomId, topicId) {
60
+ const values = await this.read();
61
+ delete values[pendingKey(roomId, topicId)];
62
+ if (Object.keys(values).length === 0)
63
+ await this.storage.removeItem(STORAGE_KEY);
64
+ else
65
+ await this.storage.setItem(STORAGE_KEY, JSON.stringify(values));
66
+ }
67
+ async read() {
68
+ const raw = await this.storage.getItem(STORAGE_KEY);
69
+ if (raw === null)
70
+ return {};
71
+ try {
72
+ const value = JSON.parse(raw);
73
+ const parsed = parsePendingValues(value);
74
+ if (parsed !== null)
75
+ return parsed;
76
+ }
77
+ catch (cause) {
78
+ throw new TopicProtocolError("STORAGE_FAILED", "Pending Topic storage is corrupt", {}, { cause });
79
+ }
80
+ throw new TopicProtocolError("STORAGE_FAILED", "Pending Topic storage must contain an object");
81
+ }
82
+ }
83
+ //# sourceMappingURL=pending.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending.js","sourceRoot":"","sources":["../../../src/matrix/pending.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type PendingTopicStore, type TopicCreationInput, type TopicCreationResult, type TopicMatrixTransport } from "./types.js";
2
+ export interface TopicRuntimeOptions {
3
+ readonly transport: TopicMatrixTransport;
4
+ readonly pendingStore: PendingTopicStore;
5
+ }
6
+ export declare class TopicRuntime {
7
+ #private;
8
+ constructor(options: TopicRuntimeOptions);
9
+ createTopic(input: TopicCreationInput): Promise<TopicCreationResult>;
10
+ discardPending(roomId: string, topicId: string): Promise<void>;
11
+ private create;
12
+ private resolveAnchor;
13
+ private recoverUnknownAnchor;
14
+ private createRoot;
15
+ private getPending;
16
+ private savePending;
17
+ private removePending;
18
+ }
@@ -0,0 +1,121 @@
1
+ import { TopicProtocolError } from "../errors.js";
2
+ import { assertMatrixRoomId, assertServerEventId, assertTopicMessageContent, buildTopicMessageContent, buildTopicRootContent, } from "./content.js";
3
+ import { parseTopicRootEvent } from "./events.js";
4
+ import { MATRIX_ROOM_MESSAGE, } from "./types.js";
5
+ export class TopicRuntime {
6
+ #inflight = new Map();
7
+ #transport;
8
+ #store;
9
+ constructor(options) {
10
+ this.#transport = options.transport;
11
+ this.#store = options.pendingStore;
12
+ }
13
+ createTopic(input) {
14
+ const key = `${input.roomId}\u0000${input.root.id}`;
15
+ const existing = this.#inflight.get(key);
16
+ if (existing !== undefined)
17
+ return existing;
18
+ const creation = this.create(input);
19
+ this.#inflight.set(key, creation);
20
+ void creation.then(() => this.#inflight.delete(key), () => this.#inflight.delete(key));
21
+ return creation;
22
+ }
23
+ async discardPending(roomId, topicId) {
24
+ await this.removePending(roomId, topicId);
25
+ }
26
+ async create(input) {
27
+ assertMatrixRoomId(input.roomId);
28
+ buildTopicRootContent(input.root);
29
+ assertTopicMessageContent(input.firstMessage);
30
+ const anchor = await this.resolveAnchor(input);
31
+ const messageRequest = {
32
+ roomId: input.roomId,
33
+ type: MATRIX_ROOM_MESSAGE,
34
+ content: buildTopicMessageContent(anchor, input.firstMessage),
35
+ };
36
+ let firstMessageEventId;
37
+ try {
38
+ ({ eventId: firstMessageEventId } = await this.#transport.sendEvent(messageRequest));
39
+ assertServerEventId(firstMessageEventId);
40
+ }
41
+ catch (cause) {
42
+ throw new TopicProtocolError("FIRST_MESSAGE_FAILED", "Topic root exists but the first message failed; creation can be resumed", { anchor, topicId: input.root.id }, { cause });
43
+ }
44
+ await this.removePending(input.roomId, input.root.id);
45
+ return { root: input.root, anchor, firstMessageEventId };
46
+ }
47
+ async resolveAnchor(input) {
48
+ const pending = await this.getPending(input.roomId, input.root.id);
49
+ if (pending !== null && pending.rootEventId !== null) {
50
+ assertServerEventId(pending.rootEventId);
51
+ return { roomId: input.roomId, rootEventId: pending.rootEventId };
52
+ }
53
+ if (pending !== null)
54
+ return this.recoverUnknownAnchor(input, pending);
55
+ return this.createRoot(input);
56
+ }
57
+ async recoverUnknownAnchor(input, pending) {
58
+ let discovered = null;
59
+ try {
60
+ discovered = await this.#transport.findTopicRoot?.(input.roomId, input.root.id) ?? null;
61
+ }
62
+ catch (cause) {
63
+ throw new TopicProtocolError("ROOT_DISCOVERY_FAILED", "Pending Topic root discovery failed", { pending }, { cause });
64
+ }
65
+ const parsed = discovered === null ? null : parseTopicRootEvent(discovered);
66
+ if (parsed === null || parsed.root.id !== input.root.id) {
67
+ throw new TopicProtocolError("PENDING_ROOT_UNRESOLVED", "A prior root send may have succeeded; refusing to create a duplicate", { pending });
68
+ }
69
+ await this.savePending({ ...pending, rootEventId: parsed.anchor.rootEventId });
70
+ return parsed.anchor;
71
+ }
72
+ async createRoot(input) {
73
+ const pending = {
74
+ roomId: input.roomId,
75
+ topicId: input.root.id,
76
+ rootEventId: null,
77
+ createdAt: input.root.createdAt,
78
+ };
79
+ await this.savePending(pending);
80
+ try {
81
+ const response = await this.#transport.sendEvent({
82
+ roomId: input.roomId,
83
+ type: MATRIX_ROOM_MESSAGE,
84
+ content: buildTopicRootContent(input.root),
85
+ });
86
+ assertServerEventId(response.eventId);
87
+ await this.savePending({ ...pending, rootEventId: response.eventId });
88
+ return { roomId: input.roomId, rootEventId: response.eventId };
89
+ }
90
+ catch (cause) {
91
+ if (cause instanceof TopicProtocolError)
92
+ throw cause;
93
+ throw new TopicProtocolError("ROOT_CREATION_FAILED", "Topic root creation failed", { topicId: input.root.id }, { cause });
94
+ }
95
+ }
96
+ async getPending(roomId, topicId) {
97
+ try {
98
+ return await this.#store.get(roomId, topicId);
99
+ }
100
+ catch (cause) {
101
+ throw new TopicProtocolError("STORAGE_FAILED", "Could not read pending Topic creation", { roomId, topicId }, { cause });
102
+ }
103
+ }
104
+ async savePending(pending) {
105
+ try {
106
+ await this.#store.save(pending);
107
+ }
108
+ catch (cause) {
109
+ throw new TopicProtocolError("STORAGE_FAILED", "Could not persist pending Topic creation", { pending }, { cause });
110
+ }
111
+ }
112
+ async removePending(roomId, topicId) {
113
+ try {
114
+ await this.#store.remove(roomId, topicId);
115
+ }
116
+ catch (cause) {
117
+ throw new TopicProtocolError("STORAGE_FAILED", "Could not clear pending Topic creation", { roomId, topicId }, { cause });
118
+ }
119
+ }
120
+ }
121
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/matrix/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import type { MatrixAnchor, TopicKind, TopicOperation, TopicProjection, TopicRoot } from "../projector/types.js";
2
+ export declare const MATRIX_ROOM_MESSAGE = "m.room.message";
3
+ export declare const TOPIC_METADATA_KEY = "ixo.topic";
4
+ export declare const TOPIC_ANCHOR_KEY = "ixo.topic.anchor";
5
+ export declare const TOPIC_OPERATION_EVENT_TYPE = "ixo.topic.operation";
6
+ export type MatrixEventContent = Readonly<Record<string, unknown>>;
7
+ export interface TopicMatrixEvent {
8
+ readonly eventId: string;
9
+ readonly roomId: string;
10
+ readonly type: string;
11
+ readonly sender: string;
12
+ readonly timestamp: number;
13
+ readonly content: MatrixEventContent;
14
+ readonly redacted?: boolean;
15
+ }
16
+ export interface MatrixSendRequest {
17
+ readonly roomId: string;
18
+ readonly type: string;
19
+ readonly content: MatrixEventContent;
20
+ }
21
+ export interface TopicMatrixTransport {
22
+ sendEvent(request: MatrixSendRequest): Promise<{
23
+ readonly eventId: string;
24
+ }>;
25
+ findTopicRoot?(roomId: string, topicId: string): Promise<TopicMatrixEvent | null>;
26
+ }
27
+ export interface PendingTopicCreation {
28
+ readonly roomId: string;
29
+ readonly topicId: string;
30
+ readonly rootEventId: string | null;
31
+ readonly createdAt: string;
32
+ }
33
+ export interface PendingTopicStore {
34
+ get(roomId: string, topicId: string): Promise<PendingTopicCreation | null>;
35
+ save(pending: PendingTopicCreation): Promise<void>;
36
+ remove(roomId: string, topicId: string): Promise<void>;
37
+ }
38
+ export interface TopicCreationInput {
39
+ readonly roomId: string;
40
+ readonly root: TopicRoot;
41
+ readonly firstMessage: MatrixEventContent;
42
+ }
43
+ export interface TopicCreationResult {
44
+ readonly root: TopicRoot;
45
+ readonly anchor: MatrixAnchor;
46
+ readonly firstMessageEventId: string;
47
+ }
48
+ export interface TopicRootContentOptions {
49
+ readonly replaces?: MatrixAnchor;
50
+ }
51
+ export interface ParsedTopicRoot {
52
+ readonly event: TopicMatrixEvent;
53
+ readonly root: TopicRoot;
54
+ readonly anchor: MatrixAnchor;
55
+ readonly replaces?: MatrixAnchor;
56
+ }
57
+ export type RejectedTopicEventReason = "actor-mismatch" | "invalid-content" | "invalid-relation" | "redacted" | "wrong-root";
58
+ export interface RejectedTopicEvent {
59
+ readonly eventId: string;
60
+ readonly reason: RejectedTopicEventReason;
61
+ }
62
+ export interface MatrixTopicProjection {
63
+ readonly projection: TopicProjection;
64
+ readonly rejectedEvents: readonly RejectedTopicEvent[];
65
+ }
66
+ export interface MatrixTopicProjectionInput {
67
+ readonly rootEvent: TopicMatrixEvent;
68
+ readonly operationEvents: readonly TopicMatrixEvent[];
69
+ readonly authenticateActor: (operation: TopicOperation, event: TopicMatrixEvent) => boolean;
70
+ readonly authorize: (operation: TopicOperation) => boolean;
71
+ }
72
+ export interface TopicAnchorConflict {
73
+ readonly previous: MatrixAnchor;
74
+ readonly canonical: MatrixAnchor;
75
+ readonly competing: readonly MatrixAnchor[];
76
+ }
77
+ export interface CanonicalTopicAnchor {
78
+ readonly anchor: MatrixAnchor;
79
+ readonly root: TopicRoot;
80
+ readonly conflicts: readonly TopicAnchorConflict[];
81
+ }
82
+ export interface TopicIdFactoryOptions {
83
+ readonly kind?: TopicKind;
84
+ }