@ixo/topic-protocol 1.0.0-rc.4 → 1.0.0-rc.6
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/README.md +10 -1
- package/dist/profiles/releases/1.0.0-rc.4.json +8915 -0
- package/dist/schemas/v2/capsule.schema.json +29 -0
- package/dist/schemas/v2/common.schema.json +68 -0
- package/dist/schemas/v2/operation.schema.json +308 -0
- package/dist/schemas/v2/projection.schema.json +205 -0
- package/dist/schemas/v2/record.schema.json +56 -0
- package/dist/schemas/v2/topic.schema.json +84 -0
- package/dist/src/shapes/availability.d.ts +1 -0
- package/dist/src/shapes/availability.js +3 -1
- package/dist/src/shapes/availability.js.map +1 -1
- package/dist/src/shapes/canonical.js +3 -2
- package/dist/src/shapes/canonical.js.map +1 -1
- package/dist/src/shapes/commands.js +1 -1
- package/dist/src/shapes/commands.js.map +1 -1
- package/dist/src/shapes/progress.js +14 -1
- package/dist/src/shapes/progress.js.map +1 -1
- package/dist/src/shapes/recipes.js +31 -1
- package/dist/src/shapes/recipes.js.map +1 -1
- package/dist/src/shapes/releases.d.ts +1 -1
- package/dist/src/shapes/releases.js +4 -1
- package/dist/src/shapes/releases.js.map +1 -1
- package/dist/src/shapes/resolver.js +7 -1
- package/dist/src/shapes/resolver.js.map +1 -1
- package/dist/src/v2/canonical.d.ts +3 -0
- package/dist/src/v2/canonical.js +25 -0
- package/dist/src/v2/canonical.js.map +1 -0
- package/dist/src/v2/capsule.d.ts +10 -0
- package/dist/src/v2/capsule.js +46 -0
- package/dist/src/v2/capsule.js.map +1 -0
- package/dist/src/v2/compat.d.ts +14 -0
- package/dist/src/v2/compat.js +96 -0
- package/dist/src/v2/compat.js.map +1 -0
- package/dist/src/v2/ids.d.ts +3 -0
- package/dist/src/v2/ids.js +11 -0
- package/dist/src/v2/ids.js.map +1 -0
- package/dist/src/v2/index.d.ts +9 -0
- package/dist/src/v2/index.js +9 -0
- package/dist/src/v2/index.js.map +1 -0
- package/dist/src/v2/matrix.d.ts +46 -0
- package/dist/src/v2/matrix.js +151 -0
- package/dist/src/v2/matrix.js.map +1 -0
- package/dist/src/v2/project.d.ts +14 -0
- package/dist/src/v2/project.js +268 -0
- package/dist/src/v2/project.js.map +1 -0
- package/dist/src/v2/types.d.ts +195 -0
- package/dist/src/v2/types.js +19 -0
- package/dist/src/v2/types.js.map +1 -0
- package/dist/src/v2/validate.d.ts +650 -0
- package/dist/src/v2/validate.js +45 -0
- package/dist/src/v2/validate.js.map +1 -0
- package/package.json +7 -1
- package/profiles/releases/1.0.0-rc.4.json +8915 -0
- package/recipes/agent-delivery.json +85 -1
- package/recipes/blueprint-design.json +85 -1
- package/recipes/registry.json +10 -10
- package/recipes/research-brief.json +85 -1
- package/recipes/software-build.json +85 -1
- package/recipes/verified-work-payment.json +85 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical.js","sourceRoot":"","sources":["../../../src/v2/canonical.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,MAAM,GAAG,KAA0C,CAAC;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9F,2GAA2G;IAC3G,MAAM,MAAM,GAA4B,MAAM,CAAC,MAAM,CAAC,IAAI,CAA4B,CAAC;IACvF,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9H,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,UAAU,UAAU,CAAC,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TopicCapsuleV2, TopicMessageV2, TopicProjectionV2 } from "./types.js";
|
|
2
|
+
export declare const MAX_CAPSULE_MESSAGES = 50;
|
|
3
|
+
/** One plain sentence about what would move the Topic forward. Deterministic; no viewer context. */
|
|
4
|
+
export declare function topicHintV2(topic: TopicProjectionV2): string;
|
|
5
|
+
/**
|
|
6
|
+
* Builds the bounded agent input. Messages are the host's decrypted thread
|
|
7
|
+
* messages; only the most recent `limit` are kept, oldest first. The limit is
|
|
8
|
+
* clamped to the capsule schema's range of 0 to 50.
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildTopicCapsuleV2(topic: TopicProjectionV2, messages: readonly TopicMessageV2[], limit?: number): TopicCapsuleV2;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { compareText } from "../shared/compare.js";
|
|
2
|
+
const DEFAULT_MESSAGE_LIMIT = 10;
|
|
3
|
+
export const MAX_CAPSULE_MESSAGES = 50;
|
|
4
|
+
function ownerClause(topic) {
|
|
5
|
+
return topic.owner === undefined ? "Nobody owns it" : `${topic.owner} owns it`;
|
|
6
|
+
}
|
|
7
|
+
function dueClause(topic) {
|
|
8
|
+
return topic.due === undefined ? "" : `; due ${topic.due}`;
|
|
9
|
+
}
|
|
10
|
+
/** One plain sentence about what would move the Topic forward. Deterministic; no viewer context. */
|
|
11
|
+
export function topicHintV2(topic) {
|
|
12
|
+
if (topic.status === "done")
|
|
13
|
+
return "Done.";
|
|
14
|
+
if (topic.status === "dropped")
|
|
15
|
+
return "Dropped.";
|
|
16
|
+
if (topic.blocked !== undefined) {
|
|
17
|
+
const on = topic.blocked.on === undefined ? "" : ` (waiting on ${topic.blocked.on})`;
|
|
18
|
+
return `Blocked: ${topic.blocked.reason}${on}.`;
|
|
19
|
+
}
|
|
20
|
+
if (topic.status === "waiting")
|
|
21
|
+
return "Waiting on someone else.";
|
|
22
|
+
const needsOwner = (topic.baseKind === "task" || topic.baseKind === "project") && topic.owner === undefined;
|
|
23
|
+
if (topic.status === "open") {
|
|
24
|
+
return needsOwner ? "Open. Needs an owner to start." : "Open. Set it active when work starts.";
|
|
25
|
+
}
|
|
26
|
+
return `Active. ${ownerClause(topic)}${dueClause(topic)}.`;
|
|
27
|
+
}
|
|
28
|
+
function compareMessages(left, right) {
|
|
29
|
+
return compareText(left.at, right.at) || compareText(left.eventId, right.eventId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Builds the bounded agent input. Messages are the host's decrypted thread
|
|
33
|
+
* messages; only the most recent `limit` are kept, oldest first. The limit is
|
|
34
|
+
* clamped to the capsule schema's range of 0 to 50.
|
|
35
|
+
*/
|
|
36
|
+
export function buildTopicCapsuleV2(topic, messages, limit = DEFAULT_MESSAGE_LIMIT) {
|
|
37
|
+
const ordered = [...messages].sort(compareMessages);
|
|
38
|
+
const bounded = Math.min(MAX_CAPSULE_MESSAGES, Math.max(0, Math.floor(limit)));
|
|
39
|
+
return {
|
|
40
|
+
version: 2,
|
|
41
|
+
topic: { ...topic, rejected: [] },
|
|
42
|
+
messages: ordered.slice(Math.max(0, ordered.length - bounded)),
|
|
43
|
+
hint: topicHintV2(topic),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=capsule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capsule.js","sourceRoot":"","sources":["../../../src/v2/capsule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGnD,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEvC,SAAS,WAAW,CAAC,KAAwB;IAC3C,OAAO,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,UAAU,CAAC;AACjF,CAAC;AAED,SAAS,SAAS,CAAC,KAAwB;IACzC,OAAO,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,EAAE,CAAC;AAC7D,CAAC;AAED,oGAAoG;AACpG,MAAM,UAAU,WAAW,CAAC,KAAwB;IAClD,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;QAAE,OAAO,OAAO,CAAC;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAClD,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,KAAK,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC;QACrF,OAAO,YAAY,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC;IAClD,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,0BAA0B,CAAC;IAClE,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,MAAM,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC;IAC5G,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5B,OAAO,UAAU,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,uCAAuC,CAAC;IACjG,CAAC;IACD,OAAO,WAAW,WAAW,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;AAC7D,CAAC;AAED,SAAS,eAAe,CAAC,IAAoB,EAAE,KAAqB;IAClE,OAAO,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAwB,EACxB,QAAmC,EACnC,KAAK,GAAG,qBAAqB;IAE7B,MAAM,OAAO,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO;QACL,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE;QACjC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC9D,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC;KACzB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TopicProjection, TopicRoot } from "../projector/types.js";
|
|
2
|
+
import type { TopicProjectionV2 } from "./types.js";
|
|
3
|
+
/** Author recorded on facts imported from a 1.x projection, which did not retain one. */
|
|
4
|
+
export declare const LEGACY_AUTHOR_V2 = "ixo:legacy";
|
|
5
|
+
export interface LegacyTopicV2Input {
|
|
6
|
+
readonly root: TopicRoot;
|
|
7
|
+
readonly projection: TopicProjection;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read-only view of a frozen 1.x Topic through the 2.0 shape. It never writes
|
|
11
|
+
* 2.0 events; hosts keep reading 1.x Topics with the 1.x projector and use this
|
|
12
|
+
* adapter only to list, search, or summarise them beside 2.0 Topics.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fromLegacyTopicV2(input: LegacyTopicV2Input): TopicProjectionV2;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { sha256V2 } from "./canonical.js";
|
|
2
|
+
/** Author recorded on facts imported from a 1.x projection, which did not retain one. */
|
|
3
|
+
export const LEGACY_AUTHOR_V2 = "ixo:legacy";
|
|
4
|
+
/** The 2.0 title limit; 1.x roots allowed 280 characters. */
|
|
5
|
+
const MAX_TITLE_LENGTH = 200;
|
|
6
|
+
function normaliseTitle(title) {
|
|
7
|
+
return title.length <= MAX_TITLE_LENGTH ? title : `${title.slice(0, MAX_TITLE_LENGTH - 1)}…`;
|
|
8
|
+
}
|
|
9
|
+
const STATUS_MAP = {
|
|
10
|
+
draft: "open",
|
|
11
|
+
active: "active",
|
|
12
|
+
waiting: "waiting",
|
|
13
|
+
blocked: "active",
|
|
14
|
+
resolved: "done",
|
|
15
|
+
archived: "done",
|
|
16
|
+
redirected: "dropped",
|
|
17
|
+
};
|
|
18
|
+
const BASE_KIND_MAP = {
|
|
19
|
+
project: "project",
|
|
20
|
+
task: "task",
|
|
21
|
+
question: "question",
|
|
22
|
+
discussion: "discussion",
|
|
23
|
+
decision: "question",
|
|
24
|
+
};
|
|
25
|
+
function legacyKind(root) {
|
|
26
|
+
const kind = root.kind ?? "discussion";
|
|
27
|
+
const base = BASE_KIND_MAP[kind];
|
|
28
|
+
if (base !== undefined && base === kind)
|
|
29
|
+
return { kind, baseKind: base };
|
|
30
|
+
return { kind: `legacy.${kind.replace(/_/gu, "-")}`, baseKind: base ?? "task" };
|
|
31
|
+
}
|
|
32
|
+
function legacyBlock(projection) {
|
|
33
|
+
if (projection.status !== "blocked")
|
|
34
|
+
return undefined;
|
|
35
|
+
return {
|
|
36
|
+
reason: projection.overview?.summary ?? "Blocked in Protocol 1.x",
|
|
37
|
+
by: projection.overview?.updatedBy ?? LEGACY_AUTHOR_V2,
|
|
38
|
+
since: projection.overview?.updatedAt ?? "1970-01-01T00:00:00Z",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function legacyRecordId(legacyId) {
|
|
42
|
+
const safe = legacyId.replace(/[^A-Za-z0-9._-]/gu, "-");
|
|
43
|
+
return `ixo:topic-record:${safe.padEnd(8, "0")}`;
|
|
44
|
+
}
|
|
45
|
+
function legacyDecisions(projection, createdAt) {
|
|
46
|
+
return projection.decisions.map((decision) => ({
|
|
47
|
+
version: 2,
|
|
48
|
+
id: legacyRecordId(decision.id),
|
|
49
|
+
topicId: projection.topicId,
|
|
50
|
+
type: "decision",
|
|
51
|
+
text: decision.summary,
|
|
52
|
+
author: LEGACY_AUTHOR_V2,
|
|
53
|
+
createdAt,
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
function legacyRelations(projection, at) {
|
|
57
|
+
return projection.relations.map((relation) => ({
|
|
58
|
+
relation: relation.type,
|
|
59
|
+
topicId: relation.targetTopicId,
|
|
60
|
+
by: LEGACY_AUTHOR_V2,
|
|
61
|
+
at,
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Read-only view of a frozen 1.x Topic through the 2.0 shape. It never writes
|
|
66
|
+
* 2.0 events; hosts keep reading 1.x Topics with the 1.x projector and use this
|
|
67
|
+
* adapter only to list, search, or summarise them beside 2.0 Topics.
|
|
68
|
+
*/
|
|
69
|
+
export function fromLegacyTopicV2(input) {
|
|
70
|
+
const { root, projection } = input;
|
|
71
|
+
const { kind, baseKind } = legacyKind(root);
|
|
72
|
+
const updatedAt = projection.overview?.updatedAt ?? root.createdAt;
|
|
73
|
+
const blocked = legacyBlock(projection);
|
|
74
|
+
const intent = projection.overview?.summary;
|
|
75
|
+
const state = {
|
|
76
|
+
version: 2,
|
|
77
|
+
id: projection.topicId,
|
|
78
|
+
anchor: { roomId: projection.roomId, rootEventId: projection.rootEventId },
|
|
79
|
+
title: normaliseTitle(projection.title),
|
|
80
|
+
kind,
|
|
81
|
+
baseKind,
|
|
82
|
+
status: STATUS_MAP[projection.status],
|
|
83
|
+
...(intent === undefined ? {} : { intent }),
|
|
84
|
+
...(blocked === undefined ? {} : { blocked }),
|
|
85
|
+
createdBy: root.createdBy,
|
|
86
|
+
createdAt: root.createdAt,
|
|
87
|
+
updatedBy: projection.overview?.updatedBy ?? root.createdBy,
|
|
88
|
+
updatedAt,
|
|
89
|
+
links: projection.context,
|
|
90
|
+
files: projection.attachments.map(({ provider, resource, fileId, version, cid, name, mimeType, size }) => ({ provider, resource, fileId, version, cid, name, mimeType, size })),
|
|
91
|
+
relations: legacyRelations(projection, updatedAt),
|
|
92
|
+
records: { decisions: legacyDecisions(projection, updatedAt), outcomes: [], artifacts: [], receipts: [] },
|
|
93
|
+
};
|
|
94
|
+
return { ...state, etag: sha256V2({ ...state, legacyRevision: projection.revision }), rejected: [] };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=compat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compat.js","sourceRoot":"","sources":["../../../src/v2/compat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS1C,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE7C,6DAA6D;AAC7D,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,KAAK,CAAC,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,GAAG,CAAC,CAAC,GAAG,CAAC;AAC/F,CAAC;AAED,MAAM,UAAU,GAA+D;IAC7E,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,SAAS;CACtB,CAAC;AAEF,MAAM,aAAa,GAA8C;IAC/D,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;CACrB,CAAC;AAOF,SAAS,UAAU,CAAC,IAAe;IACjC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC;IACvC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACzE,OAAO,EAAE,IAAI,EAAE,UAAU,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,IAAI,MAAM,EAAE,CAAC;AAClF,CAAC;AAED,SAAS,WAAW,CAAC,UAA2B;IAC9C,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACtD,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,IAAI,yBAAyB;QACjE,EAAE,EAAE,UAAU,CAAC,QAAQ,EAAE,SAAS,IAAI,gBAAgB;QACtD,KAAK,EAAE,UAAU,CAAC,QAAQ,EAAE,SAAS,IAAI,sBAAsB;KAChE,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,QAAgB;IACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,oBAAoB,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,eAAe,CAAC,UAA2B,EAAE,SAAiB;IACrE,OAAO,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC7C,OAAO,EAAE,CAAC;QACV,EAAE,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,UAAU,CAAC,OAAO;QAC3B,IAAI,EAAE,UAAU;QAChB,IAAI,EAAE,QAAQ,CAAC,OAAO;QACtB,MAAM,EAAE,gBAAgB;QACxB,SAAS;KACV,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,eAAe,CAAC,UAA2B,EAAE,EAAU;IAC9D,OAAO,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC7C,QAAQ,EAAE,QAAQ,CAAC,IAAI;QACvB,OAAO,EAAE,QAAQ,CAAC,aAAa;QAC/B,EAAE,EAAE,gBAAgB;QACpB,EAAE;KACH,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAyB;IACzD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IACnC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,UAAU,CAAC,QAAQ,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC;IACnE,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC5C,MAAM,KAAK,GAAiD;QAC1D,OAAO,EAAE,CAAC;QACV,EAAE,EAAE,UAAU,CAAC,OAAO;QACtB,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,WAAW,EAAE;QAC1E,KAAK,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC;QACvC,IAAI;QACJ,QAAQ;QACR,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC;QACrC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3C,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;QAC7C,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,UAAU,CAAC,QAAQ,EAAE,SAAS,IAAI,IAAI,CAAC,SAAS;QAC3D,SAAS;QACT,KAAK,EAAE,UAAU,CAAC,OAAO;QACzB,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CACxG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CACnE,CAAC;QACF,SAAS,EAAE,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC;QACjD,OAAO,EAAE,EAAE,SAAS,EAAE,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;KAC1G,CAAC;IACF,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,GAAG,KAAK,EAAE,cAAc,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AACvG,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { v7 as uuidv7 } from "uuid";
|
|
2
|
+
export function createTopicIdV2() {
|
|
3
|
+
return `ixo:topic:${uuidv7()}`;
|
|
4
|
+
}
|
|
5
|
+
export function createOperationIdV2() {
|
|
6
|
+
return `ixo:topic-op:${uuidv7()}`;
|
|
7
|
+
}
|
|
8
|
+
export function createRecordIdV2() {
|
|
9
|
+
return `ixo:topic-record:${uuidv7()}`;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../src/v2/ids.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,MAAM,UAAU,eAAe;IAC7B,OAAO,aAAa,MAAM,EAAE,EAAE,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,gBAAgB,MAAM,EAAE,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,oBAAoB,MAAM,EAAE,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { createOperationIdV2, createRecordIdV2, createTopicIdV2 } from "./ids.js";
|
|
3
|
+
export { SCHEMAS_V2, assertV2, validateV2, type SchemaNameV2, type ValidationIssueV2, type ValidationResultV2 } from "./validate.js";
|
|
4
|
+
export { baseKindOf, projectTopicV2, topicEtagV2 } from "./project.js";
|
|
5
|
+
export { canonicalJsonV2, sha256V2 } from "./canonical.js";
|
|
6
|
+
export { DEFAULT_MAX_CLOCK_SKEW_MS_V2, TOPIC_RECORD_KEY_V2, TOPIC_RECORD_MSGTYPE_V2, buildOperationContentV2, buildRecordContentV2, buildTopicRootContentV2, parseTopicEventV2, parseTopicRootV2, projectMatrixTopicV2, senderIsAuthor, senderIsCreator, type MatrixTopicProjectionInputV2, type TopicEventParseOptionsV2, type TopicRootAuthenticateV2, type MatrixTopicProjectionV2, type ParsedTopicEventV2, type ParsedTopicRootV2, type TopicAuthenticateV2, } from "./matrix.js";
|
|
7
|
+
export { MAX_CAPSULE_MESSAGES, buildTopicCapsuleV2, topicHintV2 } from "./capsule.js";
|
|
8
|
+
export { LEGACY_AUTHOR_V2, fromLegacyTopicV2, type LegacyTopicV2Input } from "./compat.js";
|
|
9
|
+
export type { TopicMatrixEvent, RejectedTopicEvent, RejectedTopicEventReason, MatrixEventContent } from "../matrix/types.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { createOperationIdV2, createRecordIdV2, createTopicIdV2 } from "./ids.js";
|
|
3
|
+
export { SCHEMAS_V2, assertV2, validateV2 } from "./validate.js";
|
|
4
|
+
export { baseKindOf, projectTopicV2, topicEtagV2 } from "./project.js";
|
|
5
|
+
export { canonicalJsonV2, sha256V2 } from "./canonical.js";
|
|
6
|
+
export { DEFAULT_MAX_CLOCK_SKEW_MS_V2, TOPIC_RECORD_KEY_V2, TOPIC_RECORD_MSGTYPE_V2, buildOperationContentV2, buildRecordContentV2, buildTopicRootContentV2, parseTopicEventV2, parseTopicRootV2, projectMatrixTopicV2, senderIsAuthor, senderIsCreator, } from "./matrix.js";
|
|
7
|
+
export { MAX_CAPSULE_MESSAGES, buildTopicCapsuleV2, topicHintV2 } from "./capsule.js";
|
|
8
|
+
export { LEGACY_AUTHOR_V2, fromLegacyTopicV2 } from "./compat.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAsE,MAAM,eAAe,CAAC;AACrI,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EACL,4BAA4B,EAC5B,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,eAAe,GAQhB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAA2B,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type MatrixEventContent, type RejectedTopicEvent, type TopicMatrixEvent } from "../matrix/types.js";
|
|
2
|
+
import type { TopicAnchorV2, TopicAuthorizeV2, TopicEntryV2, TopicManifestV2, TopicOperationV2, TopicProjectionV2, TopicRecordV2 } from "./types.js";
|
|
3
|
+
export declare const TOPIC_RECORD_MSGTYPE_V2 = "ixo.topic.record";
|
|
4
|
+
export declare const TOPIC_RECORD_KEY_V2 = "ixo.topic.record";
|
|
5
|
+
export interface ParsedTopicRootV2 {
|
|
6
|
+
readonly manifest: TopicManifestV2;
|
|
7
|
+
readonly anchor: TopicAnchorV2;
|
|
8
|
+
}
|
|
9
|
+
export type TopicAuthenticateV2 = (entry: TopicEntryV2, event: TopicMatrixEvent) => boolean;
|
|
10
|
+
export type TopicRootAuthenticateV2 = (manifest: TopicManifestV2, event: TopicMatrixEvent) => boolean;
|
|
11
|
+
/** Default clock-skew tolerance between a writer-supplied createdAt and the homeserver timestamp. */
|
|
12
|
+
export declare const DEFAULT_MAX_CLOCK_SKEW_MS_V2: number;
|
|
13
|
+
export interface TopicEventParseOptionsV2 {
|
|
14
|
+
/** Binds the claimed actor or author to the Matrix sender. Defaults to strict equality. */
|
|
15
|
+
readonly authenticate?: TopicAuthenticateV2;
|
|
16
|
+
/** Entries whose createdAt differs from the server timestamp by more than this are rejected. */
|
|
17
|
+
readonly maxClockSkewMs?: number;
|
|
18
|
+
}
|
|
19
|
+
export interface MatrixTopicProjectionInputV2 extends TopicEventParseOptionsV2 {
|
|
20
|
+
readonly rootEvent: TopicMatrixEvent;
|
|
21
|
+
readonly events: readonly TopicMatrixEvent[];
|
|
22
|
+
/** Binds the manifest's createdBy to the root sender. Defaults to strict equality. */
|
|
23
|
+
readonly authenticateRoot?: TopicRootAuthenticateV2;
|
|
24
|
+
readonly authorize: TopicAuthorizeV2;
|
|
25
|
+
}
|
|
26
|
+
export interface MatrixTopicProjectionV2 {
|
|
27
|
+
readonly projection: TopicProjectionV2;
|
|
28
|
+
readonly rejectedEvents: readonly RejectedTopicEvent[];
|
|
29
|
+
}
|
|
30
|
+
export declare function buildTopicRootContentV2(manifest: TopicManifestV2): MatrixEventContent;
|
|
31
|
+
export declare function buildOperationContentV2(operation: TopicOperationV2, rootEventId: string): MatrixEventContent;
|
|
32
|
+
export declare function buildRecordContentV2(record: TopicRecordV2, rootEventId: string): MatrixEventContent;
|
|
33
|
+
export declare function senderIsCreator(manifest: TopicManifestV2, event: TopicMatrixEvent): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Parses a root event. The manifest's createdBy must be bound to the Matrix
|
|
36
|
+
* sender by `authenticateRoot`; a root that fails that check is not a Topic.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseTopicRootV2(event: TopicMatrixEvent, authenticateRoot?: TopicRootAuthenticateV2): ParsedTopicRootV2 | null;
|
|
39
|
+
export type ParsedTopicEventV2 = {
|
|
40
|
+
readonly entry: TopicEntryV2;
|
|
41
|
+
} | {
|
|
42
|
+
readonly rejection: RejectedTopicEvent;
|
|
43
|
+
} | null;
|
|
44
|
+
export declare function senderIsAuthor(entry: TopicEntryV2, event: TopicMatrixEvent): boolean;
|
|
45
|
+
export declare function parseTopicEventV2(event: TopicMatrixEvent, anchor: TopicAnchorV2, options?: TopicEventParseOptionsV2): ParsedTopicEventV2;
|
|
46
|
+
export declare function projectMatrixTopicV2(input: MatrixTopicProjectionInputV2): MatrixTopicProjectionV2;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { TopicProtocolError } from "../errors.js";
|
|
2
|
+
import { MATRIX_ROOM_MESSAGE, TOPIC_METADATA_KEY, TOPIC_OPERATION_EVENT_TYPE, } from "../matrix/types.js";
|
|
3
|
+
import { projectTopicV2 } from "./project.js";
|
|
4
|
+
import { assertV2, validateV2 } from "./validate.js";
|
|
5
|
+
export const TOPIC_RECORD_MSGTYPE_V2 = "ixo.topic.record";
|
|
6
|
+
export const TOPIC_RECORD_KEY_V2 = "ixo.topic.record";
|
|
7
|
+
const RECORD_LABELS = {
|
|
8
|
+
decision: "Decision",
|
|
9
|
+
outcome: "Outcome",
|
|
10
|
+
artifact: "Artifact",
|
|
11
|
+
receipt: "Receipt",
|
|
12
|
+
};
|
|
13
|
+
/** Default clock-skew tolerance between a writer-supplied createdAt and the homeserver timestamp. */
|
|
14
|
+
export const DEFAULT_MAX_CLOCK_SKEW_MS_V2 = 5 * 60 * 1000;
|
|
15
|
+
export function buildTopicRootContentV2(manifest) {
|
|
16
|
+
assertV2("topic", manifest);
|
|
17
|
+
return { msgtype: "m.notice", body: `Topic: ${manifest.title}`, [TOPIC_METADATA_KEY]: manifest };
|
|
18
|
+
}
|
|
19
|
+
export function buildOperationContentV2(operation, rootEventId) {
|
|
20
|
+
assertV2("operation", operation);
|
|
21
|
+
return { operation, "m.relates_to": { rel_type: "m.reference", event_id: rootEventId } };
|
|
22
|
+
}
|
|
23
|
+
export function buildRecordContentV2(record, rootEventId) {
|
|
24
|
+
assertV2("record", record);
|
|
25
|
+
return {
|
|
26
|
+
msgtype: TOPIC_RECORD_MSGTYPE_V2,
|
|
27
|
+
body: `${RECORD_LABELS[record.type]}: ${record.text}`,
|
|
28
|
+
[TOPIC_RECORD_KEY_V2]: record,
|
|
29
|
+
"m.relates_to": {
|
|
30
|
+
rel_type: "m.thread",
|
|
31
|
+
event_id: rootEventId,
|
|
32
|
+
is_falling_back: true,
|
|
33
|
+
"m.in_reply_to": { event_id: rootEventId },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
function relationTarget(content, relType) {
|
|
41
|
+
const relation = content["m.relates_to"];
|
|
42
|
+
if (!isRecord(relation) || relation.rel_type !== relType)
|
|
43
|
+
return undefined;
|
|
44
|
+
return typeof relation.event_id === "string" ? relation.event_id : undefined;
|
|
45
|
+
}
|
|
46
|
+
export function senderIsCreator(manifest, event) {
|
|
47
|
+
return manifest.createdBy === event.sender;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Parses a root event. The manifest's createdBy must be bound to the Matrix
|
|
51
|
+
* sender by `authenticateRoot`; a root that fails that check is not a Topic.
|
|
52
|
+
*/
|
|
53
|
+
export function parseTopicRootV2(event, authenticateRoot = senderIsCreator) {
|
|
54
|
+
if (event.type !== MATRIX_ROOM_MESSAGE || event.redacted === true)
|
|
55
|
+
return null;
|
|
56
|
+
if (!event.roomId.startsWith("!") || !event.eventId.startsWith("$"))
|
|
57
|
+
return null;
|
|
58
|
+
if (event.content["m.relates_to"] !== undefined)
|
|
59
|
+
return null;
|
|
60
|
+
const manifest = event.content[TOPIC_METADATA_KEY];
|
|
61
|
+
if (!validateV2("topic", manifest).ok)
|
|
62
|
+
return null;
|
|
63
|
+
const typed = manifest;
|
|
64
|
+
if (!authenticateRoot(typed, event))
|
|
65
|
+
return null;
|
|
66
|
+
return { manifest: typed, anchor: { roomId: event.roomId, rootEventId: event.eventId } };
|
|
67
|
+
}
|
|
68
|
+
function reject(event, reason) {
|
|
69
|
+
return { rejection: { eventId: event.eventId, reason } };
|
|
70
|
+
}
|
|
71
|
+
function candidateEntry(event) {
|
|
72
|
+
if (event.type === TOPIC_OPERATION_EVENT_TYPE) {
|
|
73
|
+
const value = event.content.operation;
|
|
74
|
+
if (!validateV2("operation", value).ok)
|
|
75
|
+
return null;
|
|
76
|
+
return { entry: { kind: "operation", value: value }, relType: "m.reference" };
|
|
77
|
+
}
|
|
78
|
+
if (event.type === MATRIX_ROOM_MESSAGE && event.content.msgtype === TOPIC_RECORD_MSGTYPE_V2) {
|
|
79
|
+
const value = event.content[TOPIC_RECORD_KEY_V2];
|
|
80
|
+
if (!validateV2("record", value).ok)
|
|
81
|
+
return null;
|
|
82
|
+
return { entry: { kind: "record", value: value }, relType: "m.thread" };
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function isTopicEventShape(event) {
|
|
87
|
+
return event.type === TOPIC_OPERATION_EVENT_TYPE || event.content.msgtype === TOPIC_RECORD_MSGTYPE_V2;
|
|
88
|
+
}
|
|
89
|
+
export function senderIsAuthor(entry, event) {
|
|
90
|
+
const claimed = entry.kind === "operation" ? entry.value.actor : entry.value.author;
|
|
91
|
+
return claimed === event.sender;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Classifies one thread or reference event. Returns null for ordinary chat
|
|
95
|
+
* messages, an entry for a valid operation or record, or a rejection. An
|
|
96
|
+
* event from another room is rejected as `wrong-root` even when its relation
|
|
97
|
+
* names the root event ID. A createdAt outside the clock-skew bound of the
|
|
98
|
+
* homeserver timestamp is rejected as `invalid-content`.
|
|
99
|
+
*/
|
|
100
|
+
function withinSkew(entry, event, maxSkewMs) {
|
|
101
|
+
const claimed = Date.parse(entry.value.createdAt);
|
|
102
|
+
return !Number.isNaN(claimed) && Math.abs(claimed - event.timestamp) <= maxSkewMs;
|
|
103
|
+
}
|
|
104
|
+
export function parseTopicEventV2(event, anchor, options = {}) {
|
|
105
|
+
if (!isTopicEventShape(event))
|
|
106
|
+
return null;
|
|
107
|
+
if (event.redacted === true)
|
|
108
|
+
return reject(event, "redacted");
|
|
109
|
+
if (event.roomId !== anchor.roomId)
|
|
110
|
+
return reject(event, "wrong-root");
|
|
111
|
+
const candidate = candidateEntry(event);
|
|
112
|
+
if (candidate === null)
|
|
113
|
+
return reject(event, "invalid-content");
|
|
114
|
+
const target = relationTarget(event.content, candidate.relType);
|
|
115
|
+
if (target === undefined)
|
|
116
|
+
return reject(event, "invalid-relation");
|
|
117
|
+
if (target !== anchor.rootEventId)
|
|
118
|
+
return reject(event, "wrong-root");
|
|
119
|
+
if (!(options.authenticate ?? senderIsAuthor)(candidate.entry, event))
|
|
120
|
+
return reject(event, "actor-mismatch");
|
|
121
|
+
if (!withinSkew(candidate.entry, event, options.maxClockSkewMs ?? DEFAULT_MAX_CLOCK_SKEW_MS_V2)) {
|
|
122
|
+
return reject(event, "invalid-content");
|
|
123
|
+
}
|
|
124
|
+
return { entry: candidate.entry };
|
|
125
|
+
}
|
|
126
|
+
export function projectMatrixTopicV2(input) {
|
|
127
|
+
const root = parseTopicRootV2(input.rootEvent, input.authenticateRoot);
|
|
128
|
+
if (root === null) {
|
|
129
|
+
throw new TopicProtocolError("INVALID_MATRIX_EVENT", "Protocol 2.0 projection requires a valid, sender-authenticated, relation-free root event");
|
|
130
|
+
}
|
|
131
|
+
const operations = [];
|
|
132
|
+
const records = [];
|
|
133
|
+
const rejectedEvents = [];
|
|
134
|
+
for (const event of input.events) {
|
|
135
|
+
const parsed = parseTopicEventV2(event, root.anchor, input);
|
|
136
|
+
if (parsed === null)
|
|
137
|
+
continue;
|
|
138
|
+
if ("rejection" in parsed) {
|
|
139
|
+
rejectedEvents.push(parsed.rejection);
|
|
140
|
+
}
|
|
141
|
+
else if (parsed.entry.kind === "operation") {
|
|
142
|
+
operations.push(parsed.entry.value);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
records.push(parsed.entry.value);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const projection = projectTopicV2({ manifest: root.manifest, anchor: root.anchor, operations, records, authorize: input.authorize });
|
|
149
|
+
return { projection, rejectedEvents };
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=matrix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../../src/v2/matrix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { TopicBaseKindV2, TopicManifestV2, TopicProjectionInputV2, TopicProjectionV2 } from "./types.js";
|
|
2
|
+
export declare function baseKindOf(manifest: Pick<TopicManifestV2, "kind" | "baseKind">): TopicBaseKindV2;
|
|
3
|
+
/** Recomputes the concurrency token from a projection; equal to `projection.etag` when the projection is untouched. */
|
|
4
|
+
export declare function topicEtagV2(projection: TopicProjectionV2): string;
|
|
5
|
+
/**
|
|
6
|
+
* The only projector in Protocol 2.0. Folds a manifest, its operations, and
|
|
7
|
+
* its records into one deterministic projection. Ordering is by the createdAt
|
|
8
|
+
* instant, then identifier in byte order; later writes win per field. An
|
|
9
|
+
* identifier that appears with two different admissible payloads is a conflict
|
|
10
|
+
* and every admissible entry carrying it is rejected, whatever the arrival
|
|
11
|
+
* order. Entries that fail the topic or authorization check are rejected on
|
|
12
|
+
* their own and cannot create a conflict for anyone else.
|
|
13
|
+
*/
|
|
14
|
+
export declare function projectTopicV2(input: TopicProjectionInputV2): TopicProjectionV2;
|