@ccmsg/protocol 1.22.1 → 2.0.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.
- package/package.json +1 -1
- package/src/attributes.ts +68 -68
- package/src/common/auth.ts +4 -4
- package/src/common/hello.ts +38 -5
- package/src/common/ping.ts +2 -2
- package/src/common/topics.ts +6 -0
- package/src/control/agents.ts +20 -11
- package/src/control/dump.ts +22 -1
- package/src/control/peers.ts +124 -34
- package/src/control/session.ts +24 -9
- package/src/envelope.ts +2 -2
- package/src/errors.ts +13 -3
- package/src/fixtures/common.ts +5 -4
- package/src/fixtures/control.ts +1 -1
- package/src/fixtures/messaging.ts +1 -0
- package/src/fixtures/topics.ts +67 -9
- package/src/identifiers.ts +16 -1
- package/src/messaging/message.ts +65 -4
- package/src/messaging/notify.ts +12 -1
package/src/messaging/message.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { InstanceId, Mid, Sender, Sid, Timestamp } from "../identifiers.ts";
|
|
|
7
7
|
* These are not errors: the op succeeded and the message is held in the
|
|
8
8
|
* recipient's inbox. They tell the sender what to do next — wait, resend to
|
|
9
9
|
* another session, or give up. The op itself fails only when `to` names no
|
|
10
|
-
* session anywhere in the
|
|
10
|
+
* session anywhere in the mesh (`session_not_found`). */
|
|
11
11
|
export const UndeliveredReason = Type.Union(
|
|
12
12
|
[
|
|
13
13
|
/** Alive, but not yet listening. The daemon delivers when it starts. */
|
|
@@ -89,11 +89,57 @@ export const InboxMessage = Type.Object(
|
|
|
89
89
|
text: Type.String(),
|
|
90
90
|
reply_to: Type.Optional(Mid),
|
|
91
91
|
sent_at: Timestamp,
|
|
92
|
+
/** Who it is addressed to. A session's own subscription is already the
|
|
93
|
+
* recipient, so a row reaching one says nothing by repeating it; a person
|
|
94
|
+
* holds the inbox of every session in one subscription, and a row that
|
|
95
|
+
* does not name its recipient cannot be placed against any of them. So the
|
|
96
|
+
* instance states it on the rows it answers a person with. */
|
|
97
|
+
to: Type.Optional(Sid),
|
|
92
98
|
},
|
|
93
99
|
{ $id: "InboxMessage" },
|
|
94
100
|
);
|
|
95
101
|
export type InboxMessage = Static<typeof InboxMessage>;
|
|
96
102
|
|
|
103
|
+
/** Why a message is no longer in the inbox.
|
|
104
|
+
*
|
|
105
|
+
* The three are apart because they are three different things to have happened
|
|
106
|
+
* to a message, and a reader watching for one it sent draws each differently:
|
|
107
|
+
* `delivered` means the recipient has it and its own account of it follows,
|
|
108
|
+
* while the other two mean it never arrived and never will. A removal with no
|
|
109
|
+
* reason would leave a waiting message and an abandoned one looking alike. */
|
|
110
|
+
export const InboxRemovedReason = Type.Union(
|
|
111
|
+
[
|
|
112
|
+
/** Handed to the recipient. */
|
|
113
|
+
Type.Literal("delivered"),
|
|
114
|
+
/** `INBOX_RETENTION_MS` ran out with the recipient never taking it. */
|
|
115
|
+
Type.Literal("expired"),
|
|
116
|
+
/** Dropped, oldest first, to take a newer message into a full inbox —
|
|
117
|
+
* the same event the newer message's sender was told as `inbox_full`. */
|
|
118
|
+
Type.Literal("dropped"),
|
|
119
|
+
],
|
|
120
|
+
{ $id: "InboxRemovedReason" },
|
|
121
|
+
);
|
|
122
|
+
export type InboxRemovedReason = Static<typeof InboxRemovedReason>;
|
|
123
|
+
|
|
124
|
+
/** A message that has left the inbox.
|
|
125
|
+
*
|
|
126
|
+
* A removal has to be a marked element rather than an absence, since a frame
|
|
127
|
+
* carries only what changed and an absence in it says nothing. It names the
|
|
128
|
+
* `mid` every row is matched by, and why — there is no message left to
|
|
129
|
+
* describe, and the reason is the one thing the reader cannot derive. */
|
|
130
|
+
export const InboxRemoved = Type.Object(
|
|
131
|
+
{
|
|
132
|
+
mid: Mid,
|
|
133
|
+
removed: Type.Literal(true),
|
|
134
|
+
reason: InboxRemovedReason,
|
|
135
|
+
},
|
|
136
|
+
{ $id: "InboxRemoved" },
|
|
137
|
+
);
|
|
138
|
+
export type InboxRemoved = Static<typeof InboxRemoved>;
|
|
139
|
+
|
|
140
|
+
export const InboxElement = Type.Union([InboxMessage, InboxRemoved], { $id: "InboxElement" });
|
|
141
|
+
export type InboxElement = Static<typeof InboxElement>;
|
|
142
|
+
|
|
97
143
|
/** How long an undelivered message is kept for its recipient. The same window
|
|
98
144
|
* a lost session stays listed for: a message outliving the session it was
|
|
99
145
|
* addressed to would be offered to no one, and a session outliving what was
|
|
@@ -106,6 +152,21 @@ export const INBOX_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
|
106
152
|
* accepts is one the recipient can still be handed. */
|
|
107
153
|
export const INBOX_MAX_PER_SID = 256;
|
|
108
154
|
|
|
109
|
-
/** The `inbox` topic
|
|
110
|
-
*
|
|
111
|
-
|
|
155
|
+
/** The `inbox` topic: what is waiting, for whoever may see it.
|
|
156
|
+
*
|
|
157
|
+
* Elements, matched by `mid`. The snapshot is what is still undelivered and
|
|
158
|
+
* each later frame is what changed — a message arriving, or one leaving as an
|
|
159
|
+
* `InboxRemoved`.
|
|
160
|
+
*
|
|
161
|
+
* **A session's subscription is the delivery and a person's is a view.** What
|
|
162
|
+
* the session is handed it has been given, and the message leaves its inbox;
|
|
163
|
+
* what a person reads leaves the inbox exactly as it was, because a person is
|
|
164
|
+
* not who any of it was addressed to. The asymmetry is the point rather than an
|
|
165
|
+
* exception: without the view there is no way to see that something sent is
|
|
166
|
+
* still waiting, and a view that consumed what it looked at would deliver
|
|
167
|
+
* messages to no one by being opened.
|
|
168
|
+
*
|
|
169
|
+
* A person therefore sees a message twice over: waiting here, and afterwards in
|
|
170
|
+
* the recipient's own transcript. `mid` is what joins the two, and the removal
|
|
171
|
+
* marked `delivered` is what says the second is coming. */
|
|
172
|
+
export const InboxFrame = topicFrame("inbox", Type.Array(InboxElement));
|
package/src/messaging/notify.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Static, Type } from "@sinclair/typebox";
|
|
2
2
|
import { request, response, topicFrame } from "../envelope.ts";
|
|
3
|
-
import { Sid, Timestamp } from "../identifiers.ts";
|
|
3
|
+
import { Mid, Sid, Timestamp } from "../identifiers.ts";
|
|
4
4
|
|
|
5
5
|
/** A short line meant to reach a person watching, not the session's own turn.
|
|
6
6
|
* Delivery is best effort and unacknowledged; unlike `message.send`, nothing is
|
|
@@ -9,6 +9,8 @@ export const NotifySendArgs = Type.Object({
|
|
|
9
9
|
/** The session the notification is about. Omit to mean the caller. */
|
|
10
10
|
sid: Type.Optional(Sid),
|
|
11
11
|
text: Type.String({ minLength: 1 }),
|
|
12
|
+
/** The `mid` this line answers, when it answers one. */
|
|
13
|
+
reply_to: Type.Optional(Mid),
|
|
12
14
|
});
|
|
13
15
|
export type NotifySendArgs = Static<typeof NotifySendArgs>;
|
|
14
16
|
|
|
@@ -24,6 +26,15 @@ export const Notification = Type.Object(
|
|
|
24
26
|
/** How the session should be shown, resolved by the issuing instance. */
|
|
25
27
|
sid_label: Type.String(),
|
|
26
28
|
text: Type.String(),
|
|
29
|
+
/** What this line answers, when it answers something. A notification is
|
|
30
|
+
* shown while the session's own account of the same answer is still being
|
|
31
|
+
* written, so a reader holding both needs to know they are one thing: the
|
|
32
|
+
* `mid` is the key it matches on, and without it the two stand as two.
|
|
33
|
+
*
|
|
34
|
+
* It says what is answered and never what kind of line this is. A
|
|
35
|
+
* notification is one thing whoever it came from, and a kind would be read
|
|
36
|
+
* as a reason to draw it differently. */
|
|
37
|
+
reply_to: Type.Optional(Mid),
|
|
27
38
|
sent_at: Timestamp,
|
|
28
39
|
},
|
|
29
40
|
{ $id: "Notification" },
|