@moqtap/codec 0.1.0 → 0.1.1
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/dist/{chunk-YBSEOSSP.js → chunk-A27S7HW7.js} +5 -1
- package/dist/{chunk-5WFXFLL4.cjs → chunk-FUFTMAQD.cjs} +96 -63
- package/dist/{chunk-2NARXGVA.cjs → chunk-FWISIR26.cjs} +5 -1
- package/dist/{chunk-23YG7F46.js → chunk-IXHOBNXA.js} +117 -17
- package/dist/{chunk-3BSZ55L3.cjs → chunk-NLYTRGXA.cjs} +153 -19
- package/dist/{chunk-GDRGWFEK.cjs → chunk-NPWHHWXT.cjs} +249 -37
- package/dist/{chunk-IQPDRQVC.js → chunk-U2B3B42P.js} +62 -29
- package/dist/{chunk-WNTXF3DE.cjs → chunk-YBZD3DU5.cjs} +127 -27
- package/dist/{chunk-DC4L6ZIT.js → chunk-YTXLWKOR.js} +153 -19
- package/dist/{chunk-YPXLV5YK.js → chunk-Z66WDWHI.js} +249 -37
- package/dist/{codec-qPzfmLNu.d.ts → codec-B2mc2g3i.d.ts} +5 -5
- package/dist/{codec-CTvFtQQI.d.cts → codec-Bvr7rFtj.d.cts} +5 -5
- package/dist/draft14-session.cjs +2 -2
- package/dist/draft14-session.d.cts +4 -4
- package/dist/draft14-session.d.ts +4 -4
- package/dist/draft14-session.js +1 -1
- package/dist/draft14.cjs +4 -4
- package/dist/draft14.d.cts +15 -15
- package/dist/draft14.d.ts +15 -15
- package/dist/draft14.js +3 -3
- package/dist/draft7-session.cjs +2 -2
- package/dist/draft7-session.d.cts +3 -3
- package/dist/draft7-session.d.ts +3 -3
- package/dist/draft7-session.js +1 -1
- package/dist/draft7.cjs +5 -5
- package/dist/draft7.d.cts +10 -10
- package/dist/draft7.d.ts +10 -10
- package/dist/draft7.js +2 -2
- package/dist/index.cjs +6 -6
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +3 -3
- package/dist/{session-types-B9NIf7_F.d.ts → session-types-DFjMk4HH.d.ts} +20 -20
- package/dist/{session-types-CCo-oA-d.d.cts → session-types-DW1RSZX_.d.cts} +20 -20
- package/dist/session.cjs +4 -4
- package/dist/session.d.cts +3 -3
- package/dist/session.d.ts +3 -3
- package/dist/session.js +2 -2
- package/dist/{types-CIk5W10V.d.ts → types-BTFeKYCb.d.cts} +37 -37
- package/dist/{types-CIk5W10V.d.cts → types-BTFeKYCb.d.ts} +37 -37
- package/dist/{types-ClXELFGN.d.cts → types-DPYE49t0.d.cts} +36 -36
- package/dist/{types-ClXELFGN.d.ts → types-DPYE49t0.d.ts} +36 -36
- package/package.json +7 -7
- package/src/core/buffer-reader.ts +16 -9
- package/src/core/buffer-writer.ts +2 -2
- package/src/core/errors.ts +1 -1
- package/src/core/session-types.ts +28 -41
- package/src/core/types.ts +70 -70
- package/src/drafts/draft07/announce-fsm.ts +1 -1
- package/src/drafts/draft07/codec.ts +195 -86
- package/src/drafts/draft07/index.ts +43 -44
- package/src/drafts/draft07/messages.ts +1 -1
- package/src/drafts/draft07/parameters.ts +2 -2
- package/src/drafts/draft07/rules.ts +68 -37
- package/src/drafts/draft07/session-fsm.ts +330 -117
- package/src/drafts/draft07/session.ts +10 -10
- package/src/drafts/draft07/subscription-fsm.ts +1 -1
- package/src/drafts/draft07/varint.ts +4 -4
- package/src/drafts/draft14/codec.ts +339 -189
- package/src/drafts/draft14/index.ts +103 -108
- package/src/drafts/draft14/messages.ts +61 -61
- package/src/drafts/draft14/rules.ts +77 -34
- package/src/drafts/draft14/session-fsm.ts +458 -146
- package/src/drafts/draft14/session.ts +13 -13
- package/src/drafts/draft14/types.ts +68 -68
- package/src/index.ts +66 -31
- package/src/session.ts +20 -20
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import type { Draft14Message, Draft14MessageType } from './types.js';
|
|
2
1
|
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
ValidationResult,
|
|
2
|
+
AnnounceState,
|
|
3
|
+
FetchState,
|
|
6
4
|
ProtocolViolation,
|
|
5
|
+
PublishState,
|
|
6
|
+
SessionPhase,
|
|
7
7
|
SideEffect,
|
|
8
8
|
SubscriptionState,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
TransitionResult,
|
|
10
|
+
ValidationResult,
|
|
11
|
+
} from "../../core/session-types.js";
|
|
12
|
+
import {
|
|
13
|
+
CLIENT_ONLY_MESSAGES,
|
|
14
|
+
getLegalIncoming,
|
|
15
|
+
getLegalOutgoing,
|
|
16
|
+
SERVER_ONLY_MESSAGES,
|
|
17
|
+
} from "./rules.js";
|
|
18
|
+
import type { Draft14Message, Draft14MessageType } from "./types.js";
|
|
14
19
|
|
|
15
20
|
function violation(
|
|
16
|
-
code: ProtocolViolation<Draft14MessageType>[
|
|
21
|
+
code: ProtocolViolation<Draft14MessageType>["code"],
|
|
17
22
|
message: string,
|
|
18
23
|
currentPhase: SessionPhase,
|
|
19
24
|
offendingMessage: Draft14MessageType,
|
|
@@ -22,23 +27,35 @@ function violation(
|
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export class Draft14SessionFSM {
|
|
25
|
-
private _phase: SessionPhase =
|
|
26
|
-
private _role:
|
|
30
|
+
private _phase: SessionPhase = "idle";
|
|
31
|
+
private _role: "client" | "server";
|
|
27
32
|
private _subscriptions = new Map<bigint, SubscriptionState>();
|
|
28
33
|
private _publishes = new Map<bigint, PublishState>();
|
|
29
34
|
private _fetches = new Map<bigint, FetchState>();
|
|
30
35
|
private _requestIds = new Set<bigint>();
|
|
31
36
|
|
|
32
|
-
constructor(role:
|
|
37
|
+
constructor(role: "client" | "server") {
|
|
33
38
|
this._role = role;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
get phase(): SessionPhase {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
get
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
get phase(): SessionPhase {
|
|
42
|
+
return this._phase;
|
|
43
|
+
}
|
|
44
|
+
get role(): "client" | "server" {
|
|
45
|
+
return this._role;
|
|
46
|
+
}
|
|
47
|
+
get subscriptions(): ReadonlyMap<bigint, SubscriptionState> {
|
|
48
|
+
return this._subscriptions;
|
|
49
|
+
}
|
|
50
|
+
get announces(): ReadonlyMap<string, AnnounceState> {
|
|
51
|
+
return new Map();
|
|
52
|
+
}
|
|
53
|
+
get publishes(): ReadonlyMap<bigint, PublishState> {
|
|
54
|
+
return this._publishes;
|
|
55
|
+
}
|
|
56
|
+
get fetches(): ReadonlyMap<bigint, FetchState> {
|
|
57
|
+
return this._fetches;
|
|
58
|
+
}
|
|
42
59
|
|
|
43
60
|
get legalOutgoing(): ReadonlySet<Draft14MessageType> {
|
|
44
61
|
return getLegalOutgoing(this._phase, this._role);
|
|
@@ -49,41 +66,73 @@ export class Draft14SessionFSM {
|
|
|
49
66
|
}
|
|
50
67
|
|
|
51
68
|
// Validate role constraints
|
|
52
|
-
private checkRole(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
private checkRole(
|
|
70
|
+
message: Draft14Message,
|
|
71
|
+
direction: "inbound" | "outbound",
|
|
72
|
+
): ProtocolViolation<Draft14MessageType> | null {
|
|
73
|
+
const senderRole =
|
|
74
|
+
direction === "outbound" ? this._role : this._role === "client" ? "server" : "client";
|
|
75
|
+
|
|
76
|
+
if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== "client") {
|
|
77
|
+
return violation(
|
|
78
|
+
"ROLE_VIOLATION",
|
|
79
|
+
`${message.type} can only be sent by client`,
|
|
80
|
+
this._phase,
|
|
81
|
+
message.type,
|
|
82
|
+
);
|
|
57
83
|
}
|
|
58
|
-
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !==
|
|
59
|
-
return violation(
|
|
84
|
+
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== "server") {
|
|
85
|
+
return violation(
|
|
86
|
+
"ROLE_VIOLATION",
|
|
87
|
+
`${message.type} can only be sent by server`,
|
|
88
|
+
this._phase,
|
|
89
|
+
message.type,
|
|
90
|
+
);
|
|
60
91
|
}
|
|
61
92
|
return null;
|
|
62
93
|
}
|
|
63
94
|
|
|
64
|
-
private checkDuplicateRequestId(
|
|
95
|
+
private checkDuplicateRequestId(
|
|
96
|
+
requestId: bigint,
|
|
97
|
+
msgType: Draft14MessageType,
|
|
98
|
+
): ProtocolViolation<Draft14MessageType> | null {
|
|
65
99
|
if (this._requestIds.has(requestId)) {
|
|
66
|
-
return violation(
|
|
100
|
+
return violation(
|
|
101
|
+
"DUPLICATE_REQUEST_ID",
|
|
102
|
+
`Request ID ${requestId} already in use`,
|
|
103
|
+
this._phase,
|
|
104
|
+
msgType,
|
|
105
|
+
);
|
|
67
106
|
}
|
|
68
107
|
return null;
|
|
69
108
|
}
|
|
70
109
|
|
|
71
|
-
private checkKnownRequestId(
|
|
110
|
+
private checkKnownRequestId(
|
|
111
|
+
requestId: bigint,
|
|
112
|
+
msgType: Draft14MessageType,
|
|
113
|
+
): ProtocolViolation<Draft14MessageType> | null {
|
|
72
114
|
if (!this._requestIds.has(requestId)) {
|
|
73
|
-
return violation(
|
|
115
|
+
return violation(
|
|
116
|
+
"UNKNOWN_REQUEST_ID",
|
|
117
|
+
`No request with ID ${requestId}`,
|
|
118
|
+
this._phase,
|
|
119
|
+
msgType,
|
|
120
|
+
);
|
|
74
121
|
}
|
|
75
122
|
return null;
|
|
76
123
|
}
|
|
77
124
|
|
|
78
125
|
validateOutgoing(message: Draft14Message): ValidationResult<Draft14MessageType> {
|
|
79
|
-
const roleViolation = this.checkRole(message,
|
|
126
|
+
const roleViolation = this.checkRole(message, "outbound");
|
|
80
127
|
if (roleViolation) return { ok: false, violation: roleViolation };
|
|
81
128
|
|
|
82
129
|
if (!this.legalOutgoing.has(message.type)) {
|
|
83
130
|
return {
|
|
84
131
|
ok: false,
|
|
85
132
|
violation: violation(
|
|
86
|
-
this._phase ===
|
|
133
|
+
this._phase === "idle" || this._phase === "setup"
|
|
134
|
+
? "MESSAGE_BEFORE_SETUP"
|
|
135
|
+
: "UNEXPECTED_MESSAGE",
|
|
87
136
|
`Cannot send ${message.type} in phase ${this._phase}`,
|
|
88
137
|
this._phase,
|
|
89
138
|
message.type,
|
|
@@ -94,60 +143,63 @@ export class Draft14SessionFSM {
|
|
|
94
143
|
}
|
|
95
144
|
|
|
96
145
|
receive(message: Draft14Message): TransitionResult<Draft14MessageType> {
|
|
97
|
-
const roleViolation = this.checkRole(message,
|
|
146
|
+
const roleViolation = this.checkRole(message, "inbound");
|
|
98
147
|
if (roleViolation) return { ok: false, violation: roleViolation };
|
|
99
148
|
|
|
100
|
-
return this.applyTransition(message,
|
|
149
|
+
return this.applyTransition(message, "inbound");
|
|
101
150
|
}
|
|
102
151
|
|
|
103
152
|
send(message: Draft14Message): TransitionResult<Draft14MessageType> {
|
|
104
|
-
const roleViolation = this.checkRole(message,
|
|
153
|
+
const roleViolation = this.checkRole(message, "outbound");
|
|
105
154
|
if (roleViolation) return { ok: false, violation: roleViolation };
|
|
106
155
|
|
|
107
|
-
return this.applyTransition(message,
|
|
156
|
+
return this.applyTransition(message, "outbound");
|
|
108
157
|
}
|
|
109
158
|
|
|
110
|
-
private applyTransition(
|
|
159
|
+
private applyTransition(
|
|
160
|
+
message: Draft14Message,
|
|
161
|
+
direction: "inbound" | "outbound",
|
|
162
|
+
): TransitionResult<Draft14MessageType> {
|
|
111
163
|
const sideEffects: SideEffect[] = [];
|
|
112
164
|
|
|
113
165
|
switch (message.type) {
|
|
114
|
-
case
|
|
166
|
+
case "client_setup":
|
|
115
167
|
return this.handleClientSetup(message, direction);
|
|
116
|
-
case
|
|
168
|
+
case "server_setup":
|
|
117
169
|
return this.handleServerSetup(message, direction);
|
|
118
|
-
case
|
|
170
|
+
case "goaway":
|
|
119
171
|
return this.handleGoAway(message, direction, sideEffects);
|
|
120
172
|
|
|
121
173
|
// Subscribe lifecycle
|
|
122
|
-
case
|
|
174
|
+
case "subscribe":
|
|
123
175
|
return this.handleSubscribe(message, direction, sideEffects);
|
|
124
|
-
case
|
|
176
|
+
case "subscribe_ok":
|
|
125
177
|
return this.handleSubscribeOk(message, direction, sideEffects);
|
|
126
|
-
case
|
|
178
|
+
case "subscribe_error":
|
|
127
179
|
return this.handleSubscribeError(message, direction, sideEffects);
|
|
128
|
-
case
|
|
180
|
+
case "subscribe_update":
|
|
129
181
|
return this.handleSubscribeUpdate(message, direction, sideEffects);
|
|
130
|
-
case
|
|
182
|
+
case "unsubscribe":
|
|
131
183
|
return this.handleUnsubscribe(message, direction, sideEffects);
|
|
132
184
|
|
|
133
185
|
// Publish lifecycle
|
|
134
|
-
case
|
|
186
|
+
case "publish":
|
|
135
187
|
return this.handlePublish(message, direction, sideEffects);
|
|
136
|
-
case
|
|
188
|
+
case "publish_ok":
|
|
137
189
|
return this.handlePublishOk(message, direction, sideEffects);
|
|
138
|
-
case
|
|
190
|
+
case "publish_error":
|
|
139
191
|
return this.handlePublishError(message, direction, sideEffects);
|
|
140
|
-
case
|
|
192
|
+
case "publish_done":
|
|
141
193
|
return this.handlePublishDone(message, direction, sideEffects);
|
|
142
194
|
|
|
143
195
|
// Fetch lifecycle
|
|
144
|
-
case
|
|
196
|
+
case "fetch":
|
|
145
197
|
return this.handleFetch(message, direction, sideEffects);
|
|
146
|
-
case
|
|
198
|
+
case "fetch_ok":
|
|
147
199
|
return this.handleFetchOk(message, direction, sideEffects);
|
|
148
|
-
case
|
|
200
|
+
case "fetch_error":
|
|
149
201
|
return this.handleFetchError(message, direction, sideEffects);
|
|
150
|
-
case
|
|
202
|
+
case "fetch_cancel":
|
|
151
203
|
return this.handleFetchCancel(message, direction, sideEffects);
|
|
152
204
|
|
|
153
205
|
// Publish namespace, subscribe namespace, track status, and other ready-phase messages
|
|
@@ -156,46 +208,98 @@ export class Draft14SessionFSM {
|
|
|
156
208
|
}
|
|
157
209
|
}
|
|
158
210
|
|
|
159
|
-
private handleClientSetup(
|
|
160
|
-
|
|
161
|
-
|
|
211
|
+
private handleClientSetup(
|
|
212
|
+
_message: Draft14Message,
|
|
213
|
+
direction: "inbound" | "outbound",
|
|
214
|
+
): TransitionResult<Draft14MessageType> {
|
|
215
|
+
if (this._phase !== "idle") {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
violation: violation(
|
|
219
|
+
"SETUP_VIOLATION",
|
|
220
|
+
"CLIENT_SETUP already sent/received",
|
|
221
|
+
this._phase,
|
|
222
|
+
"client_setup",
|
|
223
|
+
),
|
|
224
|
+
};
|
|
162
225
|
}
|
|
163
226
|
|
|
164
|
-
if (direction ===
|
|
165
|
-
return {
|
|
227
|
+
if (direction === "outbound" && this._role !== "client") {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
violation: violation(
|
|
231
|
+
"ROLE_VIOLATION",
|
|
232
|
+
"Only client can send CLIENT_SETUP",
|
|
233
|
+
this._phase,
|
|
234
|
+
"client_setup",
|
|
235
|
+
),
|
|
236
|
+
};
|
|
166
237
|
}
|
|
167
238
|
|
|
168
|
-
this._phase =
|
|
239
|
+
this._phase = "setup";
|
|
169
240
|
return { ok: true, phase: this._phase, sideEffects: [] };
|
|
170
241
|
}
|
|
171
242
|
|
|
172
|
-
private handleServerSetup(
|
|
173
|
-
|
|
174
|
-
|
|
243
|
+
private handleServerSetup(
|
|
244
|
+
_message: Draft14Message,
|
|
245
|
+
direction: "inbound" | "outbound",
|
|
246
|
+
): TransitionResult<Draft14MessageType> {
|
|
247
|
+
if (this._phase !== "setup") {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
violation: violation(
|
|
251
|
+
"SETUP_VIOLATION",
|
|
252
|
+
"SERVER_SETUP before CLIENT_SETUP",
|
|
253
|
+
this._phase,
|
|
254
|
+
"server_setup",
|
|
255
|
+
),
|
|
256
|
+
};
|
|
175
257
|
}
|
|
176
258
|
|
|
177
|
-
if (direction ===
|
|
178
|
-
return {
|
|
259
|
+
if (direction === "outbound" && this._role !== "server") {
|
|
260
|
+
return {
|
|
261
|
+
ok: false,
|
|
262
|
+
violation: violation(
|
|
263
|
+
"ROLE_VIOLATION",
|
|
264
|
+
"Only server can send SERVER_SETUP",
|
|
265
|
+
this._phase,
|
|
266
|
+
"server_setup",
|
|
267
|
+
),
|
|
268
|
+
};
|
|
179
269
|
}
|
|
180
270
|
|
|
181
|
-
this._phase =
|
|
182
|
-
return { ok: true, phase: this._phase, sideEffects: [{ type:
|
|
271
|
+
this._phase = "ready";
|
|
272
|
+
return { ok: true, phase: this._phase, sideEffects: [{ type: "session-ready" }] };
|
|
183
273
|
}
|
|
184
274
|
|
|
185
|
-
private handleGoAway(
|
|
186
|
-
|
|
187
|
-
|
|
275
|
+
private handleGoAway(
|
|
276
|
+
message: Draft14Message,
|
|
277
|
+
_direction: "inbound" | "outbound",
|
|
278
|
+
sideEffects: SideEffect[],
|
|
279
|
+
): TransitionResult<Draft14MessageType> {
|
|
280
|
+
if (this._phase !== "ready" && this._phase !== "draining") {
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
violation: violation(
|
|
284
|
+
"UNEXPECTED_MESSAGE",
|
|
285
|
+
`GOAWAY not valid in phase ${this._phase}`,
|
|
286
|
+
this._phase,
|
|
287
|
+
"goaway",
|
|
288
|
+
),
|
|
289
|
+
};
|
|
188
290
|
}
|
|
189
|
-
this._phase =
|
|
190
|
-
const goaway = message as import(
|
|
191
|
-
sideEffects.push({ type:
|
|
291
|
+
this._phase = "draining";
|
|
292
|
+
const goaway = message as import("./types.js").Draft14GoAway;
|
|
293
|
+
sideEffects.push({ type: "session-draining", goAwayUri: goaway.new_session_uri });
|
|
192
294
|
return { ok: true, phase: this._phase, sideEffects };
|
|
193
295
|
}
|
|
194
296
|
|
|
195
297
|
private requireReady(msgType: Draft14MessageType): ProtocolViolation<Draft14MessageType> | null {
|
|
196
|
-
if (this._phase !==
|
|
298
|
+
if (this._phase !== "ready" && this._phase !== "draining") {
|
|
197
299
|
return violation(
|
|
198
|
-
this._phase ===
|
|
300
|
+
this._phase === "idle" || this._phase === "setup"
|
|
301
|
+
? "MESSAGE_BEFORE_SETUP"
|
|
302
|
+
: "UNEXPECTED_MESSAGE",
|
|
199
303
|
`${msgType} requires ready phase, current: ${this._phase}`,
|
|
200
304
|
this._phase,
|
|
201
305
|
msgType,
|
|
@@ -206,18 +310,22 @@ export class Draft14SessionFSM {
|
|
|
206
310
|
|
|
207
311
|
// ─── Subscribe lifecycle ──────────────────────────────────────────────────────
|
|
208
312
|
|
|
209
|
-
private handleSubscribe(
|
|
313
|
+
private handleSubscribe(
|
|
314
|
+
message: Draft14Message,
|
|
315
|
+
_direction: "inbound" | "outbound",
|
|
316
|
+
sideEffects: SideEffect[],
|
|
317
|
+
): TransitionResult<Draft14MessageType> {
|
|
210
318
|
const err = this.requireReady(message.type);
|
|
211
319
|
if (err) return { ok: false, violation: err };
|
|
212
320
|
|
|
213
|
-
const sub = message as import(
|
|
321
|
+
const sub = message as import("./types.js").Draft14Subscribe;
|
|
214
322
|
const dupErr = this.checkDuplicateRequestId(sub.request_id, message.type);
|
|
215
323
|
if (dupErr) return { ok: false, violation: dupErr };
|
|
216
324
|
|
|
217
325
|
this._requestIds.add(sub.request_id);
|
|
218
326
|
this._subscriptions.set(sub.request_id, {
|
|
219
327
|
subscribeId: sub.request_id,
|
|
220
|
-
phase:
|
|
328
|
+
phase: "pending",
|
|
221
329
|
trackNamespace: sub.track_namespace,
|
|
222
330
|
trackName: sub.track_name,
|
|
223
331
|
});
|
|
@@ -225,240 +333,444 @@ export class Draft14SessionFSM {
|
|
|
225
333
|
return { ok: true, phase: this._phase, sideEffects };
|
|
226
334
|
}
|
|
227
335
|
|
|
228
|
-
private handleSubscribeOk(
|
|
336
|
+
private handleSubscribeOk(
|
|
337
|
+
message: Draft14Message,
|
|
338
|
+
_direction: "inbound" | "outbound",
|
|
339
|
+
sideEffects: SideEffect[],
|
|
340
|
+
): TransitionResult<Draft14MessageType> {
|
|
229
341
|
const err = this.requireReady(message.type);
|
|
230
342
|
if (err) return { ok: false, violation: err };
|
|
231
343
|
|
|
232
|
-
const ok = message as import(
|
|
344
|
+
const ok = message as import("./types.js").Draft14SubscribeOk;
|
|
233
345
|
const idErr = this.checkKnownRequestId(ok.request_id, message.type);
|
|
234
346
|
if (idErr) return { ok: false, violation: idErr };
|
|
235
347
|
|
|
236
348
|
const existing = this._subscriptions.get(ok.request_id);
|
|
237
349
|
if (!existing) {
|
|
238
|
-
return {
|
|
350
|
+
return {
|
|
351
|
+
ok: false,
|
|
352
|
+
violation: violation(
|
|
353
|
+
"UNKNOWN_REQUEST_ID",
|
|
354
|
+
`No subscription with request ID ${ok.request_id}`,
|
|
355
|
+
this._phase,
|
|
356
|
+
message.type,
|
|
357
|
+
),
|
|
358
|
+
};
|
|
239
359
|
}
|
|
240
|
-
if (existing.phase !==
|
|
241
|
-
return {
|
|
360
|
+
if (existing.phase !== "pending") {
|
|
361
|
+
return {
|
|
362
|
+
ok: false,
|
|
363
|
+
violation: violation(
|
|
364
|
+
"STATE_VIOLATION",
|
|
365
|
+
`Subscription ${ok.request_id} is ${existing.phase}, not pending`,
|
|
366
|
+
this._phase,
|
|
367
|
+
message.type,
|
|
368
|
+
),
|
|
369
|
+
};
|
|
242
370
|
}
|
|
243
371
|
|
|
244
|
-
this._subscriptions.set(ok.request_id, { ...existing, phase:
|
|
245
|
-
sideEffects.push({ type:
|
|
372
|
+
this._subscriptions.set(ok.request_id, { ...existing, phase: "active" });
|
|
373
|
+
sideEffects.push({ type: "subscription-activated", subscribeId: ok.request_id });
|
|
246
374
|
return { ok: true, phase: this._phase, sideEffects };
|
|
247
375
|
}
|
|
248
376
|
|
|
249
|
-
private handleSubscribeError(
|
|
377
|
+
private handleSubscribeError(
|
|
378
|
+
message: Draft14Message,
|
|
379
|
+
_direction: "inbound" | "outbound",
|
|
380
|
+
sideEffects: SideEffect[],
|
|
381
|
+
): TransitionResult<Draft14MessageType> {
|
|
250
382
|
const err = this.requireReady(message.type);
|
|
251
383
|
if (err) return { ok: false, violation: err };
|
|
252
384
|
|
|
253
|
-
const subErr = message as import(
|
|
385
|
+
const subErr = message as import("./types.js").Draft14SubscribeError;
|
|
254
386
|
const idErr = this.checkKnownRequestId(subErr.request_id, message.type);
|
|
255
387
|
if (idErr) return { ok: false, violation: idErr };
|
|
256
388
|
|
|
257
389
|
const existing = this._subscriptions.get(subErr.request_id);
|
|
258
390
|
if (!existing) {
|
|
259
|
-
return {
|
|
391
|
+
return {
|
|
392
|
+
ok: false,
|
|
393
|
+
violation: violation(
|
|
394
|
+
"UNKNOWN_REQUEST_ID",
|
|
395
|
+
`No subscription with request ID ${subErr.request_id}`,
|
|
396
|
+
this._phase,
|
|
397
|
+
message.type,
|
|
398
|
+
),
|
|
399
|
+
};
|
|
260
400
|
}
|
|
261
|
-
if (existing.phase !==
|
|
262
|
-
return {
|
|
401
|
+
if (existing.phase !== "pending") {
|
|
402
|
+
return {
|
|
403
|
+
ok: false,
|
|
404
|
+
violation: violation(
|
|
405
|
+
"STATE_VIOLATION",
|
|
406
|
+
`Subscription ${subErr.request_id} is ${existing.phase}, not pending`,
|
|
407
|
+
this._phase,
|
|
408
|
+
message.type,
|
|
409
|
+
),
|
|
410
|
+
};
|
|
263
411
|
}
|
|
264
412
|
|
|
265
|
-
this._subscriptions.set(subErr.request_id, { ...existing, phase:
|
|
266
|
-
sideEffects.push({
|
|
413
|
+
this._subscriptions.set(subErr.request_id, { ...existing, phase: "error" });
|
|
414
|
+
sideEffects.push({
|
|
415
|
+
type: "subscription-ended",
|
|
416
|
+
subscribeId: subErr.request_id,
|
|
417
|
+
reason: subErr.reason_phrase,
|
|
418
|
+
});
|
|
267
419
|
return { ok: true, phase: this._phase, sideEffects };
|
|
268
420
|
}
|
|
269
421
|
|
|
270
|
-
private handleSubscribeUpdate(
|
|
422
|
+
private handleSubscribeUpdate(
|
|
423
|
+
message: Draft14Message,
|
|
424
|
+
_direction: "inbound" | "outbound",
|
|
425
|
+
sideEffects: SideEffect[],
|
|
426
|
+
): TransitionResult<Draft14MessageType> {
|
|
271
427
|
const err = this.requireReady(message.type);
|
|
272
428
|
if (err) return { ok: false, violation: err };
|
|
273
429
|
|
|
274
|
-
const update = message as import(
|
|
430
|
+
const update = message as import("./types.js").Draft14SubscribeUpdate;
|
|
275
431
|
const idErr = this.checkKnownRequestId(update.request_id, message.type);
|
|
276
432
|
if (idErr) return { ok: false, violation: idErr };
|
|
277
433
|
|
|
278
434
|
const existing = this._subscriptions.get(update.request_id);
|
|
279
435
|
if (!existing) {
|
|
280
|
-
return {
|
|
436
|
+
return {
|
|
437
|
+
ok: false,
|
|
438
|
+
violation: violation(
|
|
439
|
+
"UNKNOWN_REQUEST_ID",
|
|
440
|
+
`No subscription with request ID ${update.request_id}`,
|
|
441
|
+
this._phase,
|
|
442
|
+
message.type,
|
|
443
|
+
),
|
|
444
|
+
};
|
|
281
445
|
}
|
|
282
|
-
if (existing.phase !==
|
|
283
|
-
return {
|
|
446
|
+
if (existing.phase !== "active") {
|
|
447
|
+
return {
|
|
448
|
+
ok: false,
|
|
449
|
+
violation: violation(
|
|
450
|
+
"STATE_VIOLATION",
|
|
451
|
+
`Subscription ${update.request_id} is ${existing.phase}, not active`,
|
|
452
|
+
this._phase,
|
|
453
|
+
message.type,
|
|
454
|
+
),
|
|
455
|
+
};
|
|
284
456
|
}
|
|
285
457
|
|
|
286
458
|
return { ok: true, phase: this._phase, sideEffects };
|
|
287
459
|
}
|
|
288
460
|
|
|
289
|
-
private handleUnsubscribe(
|
|
461
|
+
private handleUnsubscribe(
|
|
462
|
+
message: Draft14Message,
|
|
463
|
+
_direction: "inbound" | "outbound",
|
|
464
|
+
sideEffects: SideEffect[],
|
|
465
|
+
): TransitionResult<Draft14MessageType> {
|
|
290
466
|
const err = this.requireReady(message.type);
|
|
291
467
|
if (err) return { ok: false, violation: err };
|
|
292
468
|
|
|
293
|
-
const unsub = message as import(
|
|
469
|
+
const unsub = message as import("./types.js").Draft14Unsubscribe;
|
|
294
470
|
const idErr = this.checkKnownRequestId(unsub.request_id, message.type);
|
|
295
471
|
if (idErr) return { ok: false, violation: idErr };
|
|
296
472
|
|
|
297
473
|
const existing = this._subscriptions.get(unsub.request_id);
|
|
298
474
|
if (!existing) {
|
|
299
|
-
return {
|
|
475
|
+
return {
|
|
476
|
+
ok: false,
|
|
477
|
+
violation: violation(
|
|
478
|
+
"UNKNOWN_REQUEST_ID",
|
|
479
|
+
`No subscription with request ID ${unsub.request_id}`,
|
|
480
|
+
this._phase,
|
|
481
|
+
message.type,
|
|
482
|
+
),
|
|
483
|
+
};
|
|
300
484
|
}
|
|
301
485
|
|
|
302
|
-
this._subscriptions.set(unsub.request_id, { ...existing, phase:
|
|
303
|
-
sideEffects.push({
|
|
486
|
+
this._subscriptions.set(unsub.request_id, { ...existing, phase: "done" });
|
|
487
|
+
sideEffects.push({
|
|
488
|
+
type: "subscription-ended",
|
|
489
|
+
subscribeId: unsub.request_id,
|
|
490
|
+
reason: "unsubscribed",
|
|
491
|
+
});
|
|
304
492
|
return { ok: true, phase: this._phase, sideEffects };
|
|
305
493
|
}
|
|
306
494
|
|
|
307
495
|
// ─── Publish lifecycle ────────────────────────────────────────────────────────
|
|
308
496
|
|
|
309
|
-
private handlePublish(
|
|
497
|
+
private handlePublish(
|
|
498
|
+
message: Draft14Message,
|
|
499
|
+
_direction: "inbound" | "outbound",
|
|
500
|
+
sideEffects: SideEffect[],
|
|
501
|
+
): TransitionResult<Draft14MessageType> {
|
|
310
502
|
const err = this.requireReady(message.type);
|
|
311
503
|
if (err) return { ok: false, violation: err };
|
|
312
504
|
|
|
313
|
-
const pub = message as import(
|
|
505
|
+
const pub = message as import("./types.js").Draft14Publish;
|
|
314
506
|
const dupErr = this.checkDuplicateRequestId(pub.request_id, message.type);
|
|
315
507
|
if (dupErr) return { ok: false, violation: dupErr };
|
|
316
508
|
|
|
317
509
|
this._requestIds.add(pub.request_id);
|
|
318
510
|
this._publishes.set(pub.request_id, {
|
|
319
511
|
requestId: pub.request_id,
|
|
320
|
-
phase:
|
|
512
|
+
phase: "pending",
|
|
321
513
|
});
|
|
322
514
|
|
|
323
515
|
return { ok: true, phase: this._phase, sideEffects };
|
|
324
516
|
}
|
|
325
517
|
|
|
326
|
-
private handlePublishOk(
|
|
518
|
+
private handlePublishOk(
|
|
519
|
+
message: Draft14Message,
|
|
520
|
+
_direction: "inbound" | "outbound",
|
|
521
|
+
sideEffects: SideEffect[],
|
|
522
|
+
): TransitionResult<Draft14MessageType> {
|
|
327
523
|
const err = this.requireReady(message.type);
|
|
328
524
|
if (err) return { ok: false, violation: err };
|
|
329
525
|
|
|
330
|
-
const ok = message as import(
|
|
526
|
+
const ok = message as import("./types.js").Draft14PublishOk;
|
|
331
527
|
const idErr = this.checkKnownRequestId(ok.request_id, message.type);
|
|
332
528
|
if (idErr) return { ok: false, violation: idErr };
|
|
333
529
|
|
|
334
530
|
const existing = this._publishes.get(ok.request_id);
|
|
335
531
|
if (!existing) {
|
|
336
|
-
return {
|
|
532
|
+
return {
|
|
533
|
+
ok: false,
|
|
534
|
+
violation: violation(
|
|
535
|
+
"UNKNOWN_REQUEST_ID",
|
|
536
|
+
`No publish with request ID ${ok.request_id}`,
|
|
537
|
+
this._phase,
|
|
538
|
+
message.type,
|
|
539
|
+
),
|
|
540
|
+
};
|
|
337
541
|
}
|
|
338
|
-
if (existing.phase !==
|
|
339
|
-
return {
|
|
542
|
+
if (existing.phase !== "pending") {
|
|
543
|
+
return {
|
|
544
|
+
ok: false,
|
|
545
|
+
violation: violation(
|
|
546
|
+
"STATE_VIOLATION",
|
|
547
|
+
`Publish ${ok.request_id} is ${existing.phase}, not pending`,
|
|
548
|
+
this._phase,
|
|
549
|
+
message.type,
|
|
550
|
+
),
|
|
551
|
+
};
|
|
340
552
|
}
|
|
341
553
|
|
|
342
|
-
this._publishes.set(ok.request_id, { ...existing, phase:
|
|
343
|
-
sideEffects.push({ type:
|
|
554
|
+
this._publishes.set(ok.request_id, { ...existing, phase: "active" });
|
|
555
|
+
sideEffects.push({ type: "publish-activated", requestId: ok.request_id });
|
|
344
556
|
return { ok: true, phase: this._phase, sideEffects };
|
|
345
557
|
}
|
|
346
558
|
|
|
347
|
-
private handlePublishError(
|
|
559
|
+
private handlePublishError(
|
|
560
|
+
message: Draft14Message,
|
|
561
|
+
_direction: "inbound" | "outbound",
|
|
562
|
+
sideEffects: SideEffect[],
|
|
563
|
+
): TransitionResult<Draft14MessageType> {
|
|
348
564
|
const err = this.requireReady(message.type);
|
|
349
565
|
if (err) return { ok: false, violation: err };
|
|
350
566
|
|
|
351
|
-
const pubErr = message as import(
|
|
567
|
+
const pubErr = message as import("./types.js").Draft14PublishError;
|
|
352
568
|
const idErr = this.checkKnownRequestId(pubErr.request_id, message.type);
|
|
353
569
|
if (idErr) return { ok: false, violation: idErr };
|
|
354
570
|
|
|
355
571
|
const existing = this._publishes.get(pubErr.request_id);
|
|
356
572
|
if (!existing) {
|
|
357
|
-
return {
|
|
573
|
+
return {
|
|
574
|
+
ok: false,
|
|
575
|
+
violation: violation(
|
|
576
|
+
"UNKNOWN_REQUEST_ID",
|
|
577
|
+
`No publish with request ID ${pubErr.request_id}`,
|
|
578
|
+
this._phase,
|
|
579
|
+
message.type,
|
|
580
|
+
),
|
|
581
|
+
};
|
|
358
582
|
}
|
|
359
|
-
if (existing.phase !==
|
|
360
|
-
return {
|
|
583
|
+
if (existing.phase !== "pending") {
|
|
584
|
+
return {
|
|
585
|
+
ok: false,
|
|
586
|
+
violation: violation(
|
|
587
|
+
"STATE_VIOLATION",
|
|
588
|
+
`Publish ${pubErr.request_id} is ${existing.phase}, not pending`,
|
|
589
|
+
this._phase,
|
|
590
|
+
message.type,
|
|
591
|
+
),
|
|
592
|
+
};
|
|
361
593
|
}
|
|
362
594
|
|
|
363
|
-
this._publishes.set(pubErr.request_id, { ...existing, phase:
|
|
364
|
-
sideEffects.push({
|
|
595
|
+
this._publishes.set(pubErr.request_id, { ...existing, phase: "error" });
|
|
596
|
+
sideEffects.push({
|
|
597
|
+
type: "publish-ended",
|
|
598
|
+
requestId: pubErr.request_id,
|
|
599
|
+
reason: pubErr.reason_phrase,
|
|
600
|
+
});
|
|
365
601
|
return { ok: true, phase: this._phase, sideEffects };
|
|
366
602
|
}
|
|
367
603
|
|
|
368
|
-
private handlePublishDone(
|
|
604
|
+
private handlePublishDone(
|
|
605
|
+
message: Draft14Message,
|
|
606
|
+
_direction: "inbound" | "outbound",
|
|
607
|
+
sideEffects: SideEffect[],
|
|
608
|
+
): TransitionResult<Draft14MessageType> {
|
|
369
609
|
const err = this.requireReady(message.type);
|
|
370
610
|
if (err) return { ok: false, violation: err };
|
|
371
611
|
|
|
372
|
-
const done = message as import(
|
|
612
|
+
const done = message as import("./types.js").Draft14PublishDone;
|
|
373
613
|
const idErr = this.checkKnownRequestId(done.request_id, message.type);
|
|
374
614
|
if (idErr) return { ok: false, violation: idErr };
|
|
375
615
|
|
|
376
616
|
const existing = this._publishes.get(done.request_id);
|
|
377
617
|
if (!existing) {
|
|
378
|
-
return {
|
|
618
|
+
return {
|
|
619
|
+
ok: false,
|
|
620
|
+
violation: violation(
|
|
621
|
+
"UNKNOWN_REQUEST_ID",
|
|
622
|
+
`No publish with request ID ${done.request_id}`,
|
|
623
|
+
this._phase,
|
|
624
|
+
message.type,
|
|
625
|
+
),
|
|
626
|
+
};
|
|
379
627
|
}
|
|
380
628
|
|
|
381
|
-
this._publishes.set(done.request_id, { ...existing, phase:
|
|
382
|
-
sideEffects.push({
|
|
629
|
+
this._publishes.set(done.request_id, { ...existing, phase: "done" });
|
|
630
|
+
sideEffects.push({
|
|
631
|
+
type: "publish-ended",
|
|
632
|
+
requestId: done.request_id,
|
|
633
|
+
reason: done.reason_phrase,
|
|
634
|
+
});
|
|
383
635
|
return { ok: true, phase: this._phase, sideEffects };
|
|
384
636
|
}
|
|
385
637
|
|
|
386
638
|
// ─── Fetch lifecycle ──────────────────────────────────────────────────────────
|
|
387
639
|
|
|
388
|
-
private handleFetch(
|
|
640
|
+
private handleFetch(
|
|
641
|
+
message: Draft14Message,
|
|
642
|
+
_direction: "inbound" | "outbound",
|
|
643
|
+
sideEffects: SideEffect[],
|
|
644
|
+
): TransitionResult<Draft14MessageType> {
|
|
389
645
|
const err = this.requireReady(message.type);
|
|
390
646
|
if (err) return { ok: false, violation: err };
|
|
391
647
|
|
|
392
|
-
const fetch = message as import(
|
|
648
|
+
const fetch = message as import("./types.js").Draft14Fetch;
|
|
393
649
|
const dupErr = this.checkDuplicateRequestId(fetch.request_id, message.type);
|
|
394
650
|
if (dupErr) return { ok: false, violation: dupErr };
|
|
395
651
|
|
|
396
652
|
this._requestIds.add(fetch.request_id);
|
|
397
653
|
this._fetches.set(fetch.request_id, {
|
|
398
654
|
requestId: fetch.request_id,
|
|
399
|
-
phase:
|
|
655
|
+
phase: "pending",
|
|
400
656
|
});
|
|
401
657
|
|
|
402
658
|
return { ok: true, phase: this._phase, sideEffects };
|
|
403
659
|
}
|
|
404
660
|
|
|
405
|
-
private handleFetchOk(
|
|
661
|
+
private handleFetchOk(
|
|
662
|
+
message: Draft14Message,
|
|
663
|
+
_direction: "inbound" | "outbound",
|
|
664
|
+
sideEffects: SideEffect[],
|
|
665
|
+
): TransitionResult<Draft14MessageType> {
|
|
406
666
|
const err = this.requireReady(message.type);
|
|
407
667
|
if (err) return { ok: false, violation: err };
|
|
408
668
|
|
|
409
|
-
const ok = message as import(
|
|
669
|
+
const ok = message as import("./types.js").Draft14FetchOk;
|
|
410
670
|
const idErr = this.checkKnownRequestId(ok.request_id, message.type);
|
|
411
671
|
if (idErr) return { ok: false, violation: idErr };
|
|
412
672
|
|
|
413
673
|
const existing = this._fetches.get(ok.request_id);
|
|
414
674
|
if (!existing) {
|
|
415
|
-
return {
|
|
675
|
+
return {
|
|
676
|
+
ok: false,
|
|
677
|
+
violation: violation(
|
|
678
|
+
"UNKNOWN_REQUEST_ID",
|
|
679
|
+
`No fetch with request ID ${ok.request_id}`,
|
|
680
|
+
this._phase,
|
|
681
|
+
message.type,
|
|
682
|
+
),
|
|
683
|
+
};
|
|
416
684
|
}
|
|
417
|
-
if (existing.phase !==
|
|
418
|
-
return {
|
|
685
|
+
if (existing.phase !== "pending") {
|
|
686
|
+
return {
|
|
687
|
+
ok: false,
|
|
688
|
+
violation: violation(
|
|
689
|
+
"STATE_VIOLATION",
|
|
690
|
+
`Fetch ${ok.request_id} is ${existing.phase}, not pending`,
|
|
691
|
+
this._phase,
|
|
692
|
+
message.type,
|
|
693
|
+
),
|
|
694
|
+
};
|
|
419
695
|
}
|
|
420
696
|
|
|
421
|
-
this._fetches.set(ok.request_id, { ...existing, phase:
|
|
422
|
-
sideEffects.push({ type:
|
|
697
|
+
this._fetches.set(ok.request_id, { ...existing, phase: "active" });
|
|
698
|
+
sideEffects.push({ type: "fetch-activated", requestId: ok.request_id });
|
|
423
699
|
return { ok: true, phase: this._phase, sideEffects };
|
|
424
700
|
}
|
|
425
701
|
|
|
426
|
-
private handleFetchError(
|
|
702
|
+
private handleFetchError(
|
|
703
|
+
message: Draft14Message,
|
|
704
|
+
_direction: "inbound" | "outbound",
|
|
705
|
+
sideEffects: SideEffect[],
|
|
706
|
+
): TransitionResult<Draft14MessageType> {
|
|
427
707
|
const err = this.requireReady(message.type);
|
|
428
708
|
if (err) return { ok: false, violation: err };
|
|
429
709
|
|
|
430
|
-
const fetchErr = message as import(
|
|
710
|
+
const fetchErr = message as import("./types.js").Draft14FetchError;
|
|
431
711
|
const idErr = this.checkKnownRequestId(fetchErr.request_id, message.type);
|
|
432
712
|
if (idErr) return { ok: false, violation: idErr };
|
|
433
713
|
|
|
434
714
|
const existing = this._fetches.get(fetchErr.request_id);
|
|
435
715
|
if (!existing) {
|
|
436
|
-
return {
|
|
716
|
+
return {
|
|
717
|
+
ok: false,
|
|
718
|
+
violation: violation(
|
|
719
|
+
"UNKNOWN_REQUEST_ID",
|
|
720
|
+
`No fetch with request ID ${fetchErr.request_id}`,
|
|
721
|
+
this._phase,
|
|
722
|
+
message.type,
|
|
723
|
+
),
|
|
724
|
+
};
|
|
437
725
|
}
|
|
438
|
-
if (existing.phase !==
|
|
439
|
-
return {
|
|
726
|
+
if (existing.phase !== "pending") {
|
|
727
|
+
return {
|
|
728
|
+
ok: false,
|
|
729
|
+
violation: violation(
|
|
730
|
+
"STATE_VIOLATION",
|
|
731
|
+
`Fetch ${fetchErr.request_id} is ${existing.phase}, not pending`,
|
|
732
|
+
this._phase,
|
|
733
|
+
message.type,
|
|
734
|
+
),
|
|
735
|
+
};
|
|
440
736
|
}
|
|
441
737
|
|
|
442
|
-
this._fetches.set(fetchErr.request_id, { ...existing, phase:
|
|
443
|
-
sideEffects.push({
|
|
738
|
+
this._fetches.set(fetchErr.request_id, { ...existing, phase: "error" });
|
|
739
|
+
sideEffects.push({
|
|
740
|
+
type: "fetch-ended",
|
|
741
|
+
requestId: fetchErr.request_id,
|
|
742
|
+
reason: fetchErr.reason_phrase,
|
|
743
|
+
});
|
|
444
744
|
return { ok: true, phase: this._phase, sideEffects };
|
|
445
745
|
}
|
|
446
746
|
|
|
447
|
-
private handleFetchCancel(
|
|
747
|
+
private handleFetchCancel(
|
|
748
|
+
message: Draft14Message,
|
|
749
|
+
_direction: "inbound" | "outbound",
|
|
750
|
+
sideEffects: SideEffect[],
|
|
751
|
+
): TransitionResult<Draft14MessageType> {
|
|
448
752
|
const err = this.requireReady(message.type);
|
|
449
753
|
if (err) return { ok: false, violation: err };
|
|
450
754
|
|
|
451
|
-
const cancel = message as import(
|
|
755
|
+
const cancel = message as import("./types.js").Draft14FetchCancel;
|
|
452
756
|
const idErr = this.checkKnownRequestId(cancel.request_id, message.type);
|
|
453
757
|
if (idErr) return { ok: false, violation: idErr };
|
|
454
758
|
|
|
455
759
|
const existing = this._fetches.get(cancel.request_id);
|
|
456
760
|
if (!existing) {
|
|
457
|
-
return {
|
|
761
|
+
return {
|
|
762
|
+
ok: false,
|
|
763
|
+
violation: violation(
|
|
764
|
+
"UNKNOWN_REQUEST_ID",
|
|
765
|
+
`No fetch with request ID ${cancel.request_id}`,
|
|
766
|
+
this._phase,
|
|
767
|
+
message.type,
|
|
768
|
+
),
|
|
769
|
+
};
|
|
458
770
|
}
|
|
459
771
|
|
|
460
|
-
this._fetches.set(cancel.request_id, { ...existing, phase:
|
|
461
|
-
sideEffects.push({ type:
|
|
772
|
+
this._fetches.set(cancel.request_id, { ...existing, phase: "cancelled" });
|
|
773
|
+
sideEffects.push({ type: "fetch-ended", requestId: cancel.request_id, reason: "cancelled" });
|
|
462
774
|
return { ok: true, phase: this._phase, sideEffects };
|
|
463
775
|
}
|
|
464
776
|
|
|
@@ -471,7 +783,7 @@ export class Draft14SessionFSM {
|
|
|
471
783
|
}
|
|
472
784
|
|
|
473
785
|
reset(): void {
|
|
474
|
-
this._phase =
|
|
786
|
+
this._phase = "idle";
|
|
475
787
|
this._subscriptions.clear();
|
|
476
788
|
this._publishes.clear();
|
|
477
789
|
this._fetches.clear();
|