@cueloop/schema 0.1.0-alpha.38 → 0.1.0-alpha.39
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/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/participants.test.ts +58 -0
- package/src/participants.ts +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/schema",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.39",
|
|
4
4
|
"description": "The ReviewSession primitive: types, markdown block model, quote-primary anchors, feedback serialization",
|
|
5
5
|
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
6
|
"bugs": {
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { registerParticipant } from "./participants";
|
|
3
|
+
import { SCHEMA_VERSION, type ReviewSession } from "./types";
|
|
4
|
+
|
|
5
|
+
function emptySession(): ReviewSession {
|
|
6
|
+
return {
|
|
7
|
+
schemaVersion: SCHEMA_VERSION,
|
|
8
|
+
id: "ses_1",
|
|
9
|
+
workspace: { repoRoot: "/repo", branch: "main" },
|
|
10
|
+
artifact: { type: "plan", content: "# Plan\n", meta: {} },
|
|
11
|
+
revisions: [],
|
|
12
|
+
annotations: [],
|
|
13
|
+
verdict: null,
|
|
14
|
+
status: "pending",
|
|
15
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("registerParticipant", () => {
|
|
20
|
+
test("adds a new author with a display name", () => {
|
|
21
|
+
// Act
|
|
22
|
+
const next = registerParticipant(emptySession(), "SHA256:ana", "Ana");
|
|
23
|
+
|
|
24
|
+
// Assert
|
|
25
|
+
expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("records presence without a name (renders anonymous)", () => {
|
|
29
|
+
// Act
|
|
30
|
+
const next = registerParticipant(emptySession(), "SHA256:ana");
|
|
31
|
+
|
|
32
|
+
// Assert
|
|
33
|
+
expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh" }]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("a nameless revisit never erases a name a past visit set", () => {
|
|
37
|
+
// Arrange
|
|
38
|
+
const named = registerParticipant(emptySession(), "SHA256:ana", "Ana");
|
|
39
|
+
|
|
40
|
+
// Act
|
|
41
|
+
const next = registerParticipant(named, "SHA256:ana");
|
|
42
|
+
|
|
43
|
+
// Assert
|
|
44
|
+
expect(next).toBe(named);
|
|
45
|
+
expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("a later name updates the existing identity in place", () => {
|
|
49
|
+
// Arrange
|
|
50
|
+
const present = registerParticipant(emptySession(), "SHA256:ana");
|
|
51
|
+
|
|
52
|
+
// Act
|
|
53
|
+
const next = registerParticipant(present, "SHA256:ana", "Ana");
|
|
54
|
+
|
|
55
|
+
// Assert
|
|
56
|
+
expect(next.participants).toEqual([{ id: "SHA256:ana", provider: "ssh", name: "Ana" }]);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The participant registry: every annotation carries an `author` id, and the
|
|
3
|
+
* rail resolves that id to a display name through `session.participants`. This
|
|
4
|
+
* registers an author in that registry so a name search on "participant" or
|
|
5
|
+
* "author name" lands here.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Identity, ReviewSession } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Return the session with `author` present in the participant registry, setting
|
|
12
|
+
* the display name when one is given. A nameless call only records presence (the
|
|
13
|
+
* rail renders anonymous) and never erases a name a past visit set. Immutable:
|
|
14
|
+
* the input session is not mutated.
|
|
15
|
+
*/
|
|
16
|
+
export function registerParticipant(
|
|
17
|
+
session: ReviewSession,
|
|
18
|
+
author: string,
|
|
19
|
+
name?: string,
|
|
20
|
+
): ReviewSession {
|
|
21
|
+
const participants = session.participants ?? [];
|
|
22
|
+
const existing = participants.find((participant) => participant.id === author);
|
|
23
|
+
const trimmed = name?.trim();
|
|
24
|
+
if (existing && !trimmed) return session;
|
|
25
|
+
const next: Identity = {
|
|
26
|
+
id: author,
|
|
27
|
+
provider: "ssh",
|
|
28
|
+
...(trimmed ? { name: trimmed } : existing?.name ? { name: existing.name } : {}),
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
...session,
|
|
32
|
+
participants: existing
|
|
33
|
+
? participants.map((participant) => (participant.id === author ? next : participant))
|
|
34
|
+
: [...participants, next],
|
|
35
|
+
};
|
|
36
|
+
}
|