@moqtap/codec 0.2.3 → 0.3.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/LICENSE +21 -21
- package/README.md +100 -99
- package/dist/{chunk-RZHAPEXO.js → chunk-2A57DRXV.js} +36 -10
- package/dist/{chunk-73MWUKSJ.js → chunk-2UTVXWFF.js} +190 -102
- package/dist/{chunk-JTVCOMBI.cjs → chunk-3XKO6PCR.cjs} +290 -183
- package/dist/{chunk-ACQ4CUJP.cjs → chunk-4BDEU3D4.cjs} +237 -158
- package/dist/{chunk-4N4INRE7.cjs → chunk-4HBKHNU2.cjs} +144 -110
- package/dist/{chunk-2ZT6CVSE.js → chunk-4IVZZOY3.js} +269 -162
- package/dist/{chunk-GLBQDG4L.cjs → chunk-5O764MBD.cjs} +508 -117
- package/dist/chunk-7KBAWOBA.js +1212 -0
- package/dist/{chunk-JSQM2MG3.js → chunk-7QOPNSDF.js} +44 -12
- package/dist/{chunk-SYHW3FLI.cjs → chunk-7RE6KSIY.cjs} +28 -8
- package/dist/{chunk-YG5KJESI.js → chunk-AY6IAHUM.js} +28 -8
- package/dist/{chunk-EFM5T7OM.js → chunk-BLQDWOSG.js} +36 -10
- package/dist/{chunk-UYXTY6ZQ.cjs → chunk-E65QKRSN.cjs} +20 -12
- package/dist/{chunk-BISI45MN.cjs → chunk-EAHE3IOG.cjs} +44 -12
- package/dist/{chunk-PZBPH2D7.js → chunk-GCBFHZW6.js} +216 -137
- package/dist/{chunk-GXEW4COZ.cjs → chunk-H5NSL4MO.cjs} +36 -10
- package/dist/chunk-HZHSO7EW.cjs +1212 -0
- package/dist/{chunk-KZCYQ7ED.cjs → chunk-IILNSDEQ.cjs} +69 -41
- package/dist/{chunk-4XYGE53S.cjs → chunk-J3XCPBW3.cjs} +36 -10
- package/dist/{chunk-6AEHWULA.cjs → chunk-JKOEJZ5B.cjs} +28 -8
- package/dist/{chunk-RUPCJTC3.cjs → chunk-JL3NEW3O.cjs} +23 -1
- package/dist/{chunk-G7GI7LJA.js → chunk-LORZMJJB.js} +36 -10
- package/dist/{chunk-DUBCL3WF.cjs → chunk-LV5C6MEC.cjs} +36 -10
- package/dist/{chunk-ENURAVHI.cjs → chunk-MYXAD5JC.cjs} +44 -12
- package/dist/{chunk-FXZ2MYKJ.js → chunk-MZ7HUCGJ.js} +20 -12
- package/dist/{chunk-S74DWJE2.cjs → chunk-OKG2HT25.cjs} +277 -88
- package/dist/{chunk-BGWGVINJ.cjs → chunk-PKEOQSSZ.cjs} +144 -110
- package/dist/{chunk-2GODRVT5.js → chunk-Q46ZURKX.js} +325 -48
- package/dist/{chunk-UR6JKS56.js → chunk-Q7IGWV3P.js} +41 -9
- package/dist/{chunk-XSNX2IUJ.js → chunk-RW4UG7CN.js} +121 -87
- package/dist/{chunk-DWK5ZQZ4.js → chunk-RZTLQAKF.js} +28 -8
- package/dist/{chunk-UNS34PTA.cjs → chunk-S6IPIN66.cjs} +44 -12
- package/dist/{chunk-YIG2C3LF.cjs → chunk-SDWRXXOO.cjs} +214 -126
- package/dist/{chunk-CEH7ESNC.js → chunk-SHMA4PBY.js} +121 -87
- package/dist/{chunk-MC7W6PMI.js → chunk-U3ZVLT5L.js} +483 -135
- package/dist/{chunk-VJUUZ4W2.cjs → chunk-UJU63EFY.cjs} +374 -97
- package/dist/{chunk-7L43FE6P.js → chunk-VOU7PNHI.js} +23 -1
- package/dist/{chunk-E6E3NQYU.js → chunk-WRHGCVYN.js} +44 -12
- package/dist/{chunk-FPISPJ5N.cjs → chunk-XNWYLVGU.cjs} +529 -181
- package/dist/{chunk-U3IPZYPX.js → chunk-XS77RBY7.js} +228 -39
- package/dist/{chunk-JGG7CMSZ.cjs → chunk-XZE5R57R.cjs} +144 -110
- package/dist/{chunk-77MNXX3M.js → chunk-Y6KCSM3V.js} +121 -87
- package/dist/{chunk-TLYNOOQP.cjs → chunk-YTPZZI2M.cjs} +41 -9
- package/dist/{chunk-7DUBLRXC.js → chunk-YWRCSQBA.js} +44 -12
- package/dist/{chunk-MRJWLGYH.js → chunk-Z3VKP3PJ.js} +69 -41
- package/dist/{chunk-E6P74GDT.js → chunk-Z6U26IRI.js} +464 -73
- package/dist/{codec-FvGCxIhr.d.ts → codec-25u4_1HS.d.ts} +4 -4
- package/dist/{codec-DRcsBCsb.d.ts → codec-3Ppy_5Gd.d.ts} +4 -4
- package/dist/{codec-BdVMRMOI.d.ts → codec-BB0U8MF2.d.cts} +4 -4
- package/dist/{codec-B2cH-f5V.d.cts → codec-BHHfBTnV.d.ts} +4 -4
- package/dist/{codec-B5K73Bdj.d.ts → codec-BN6jk0Td.d.cts} +4 -4
- package/dist/{codec-R8GrCPt2.d.ts → codec-BU0gaSZm.d.ts} +4 -4
- package/dist/{codec-Bq-tLtfP.d.ts → codec-C-Yk2RNa.d.ts} +4 -4
- package/dist/codec-CFWQY9Lj.d.cts +18 -0
- package/dist/{codec-CPlK3Pi5.d.ts → codec-COy4uuPO.d.cts} +4 -4
- package/dist/codec-CewFCsEy.d.ts +18 -0
- package/dist/{codec-KXHvXZVb.d.cts → codec-Ck7ZwFF1.d.cts} +4 -4
- package/dist/{codec-DZhZSIii.d.cts → codec-D2QVSH9D.d.ts} +4 -4
- package/dist/{codec-BwFgpeQu.d.ts → codec-DDUBJGn0.d.cts} +4 -4
- package/dist/{codec-BFeliJFc.d.ts → codec-DNfml-se.d.ts} +4 -4
- package/dist/{codec-C4n1PS09.d.cts → codec-DoOGjjDq.d.cts} +4 -4
- package/dist/{codec-Bap882Fh.d.cts → codec-J7ekOSMc.d.ts} +4 -4
- package/dist/{codec-SKBeHQk7.d.ts → codec-R9TJCq-E.d.cts} +4 -4
- package/dist/{codec-DNUIgLqc.d.cts → codec-ZwV-Njt6.d.ts} +4 -4
- package/dist/{codec-HgBgHkFI.d.cts → codec-j819z_hL.d.cts} +4 -4
- package/dist/{codec-DY4QH364.d.cts → codec-w8z2j_uo.d.cts} +4 -4
- package/dist/{codec-CPvPq0sb.d.cts → codec-wtt6WF1K.d.ts} +4 -4
- package/dist/{codec-BUMjz0J4.d.cts → codec-z6_2PSzi.d.cts} +4 -4
- package/dist/draft10-session.cjs +2 -2
- package/dist/draft10-session.d.cts +6 -6
- package/dist/draft10-session.d.ts +6 -6
- package/dist/draft10-session.js +1 -1
- package/dist/draft10.cjs +8 -5
- package/dist/draft10.d.cts +14 -12
- package/dist/draft10.d.ts +14 -12
- package/dist/draft10.js +16 -13
- package/dist/draft11-session.cjs +2 -2
- package/dist/draft11-session.d.cts +6 -6
- package/dist/draft11-session.d.ts +6 -6
- package/dist/draft11-session.js +1 -1
- package/dist/draft11.cjs +8 -5
- package/dist/draft11.d.cts +14 -12
- package/dist/draft11.d.ts +14 -12
- package/dist/draft11.js +16 -13
- package/dist/draft12-session.cjs +2 -2
- package/dist/draft12-session.d.cts +6 -6
- package/dist/draft12-session.d.ts +6 -6
- package/dist/draft12-session.js +1 -1
- package/dist/draft12.cjs +8 -5
- package/dist/draft12.d.cts +16 -14
- package/dist/draft12.d.ts +16 -14
- package/dist/draft12.js +16 -13
- package/dist/draft13-session.cjs +2 -2
- package/dist/draft13-session.d.cts +6 -6
- package/dist/draft13-session.d.ts +6 -6
- package/dist/draft13-session.js +1 -1
- package/dist/draft13.cjs +8 -5
- package/dist/draft13.d.cts +16 -14
- package/dist/draft13.d.ts +16 -14
- package/dist/draft13.js +16 -13
- package/dist/draft14-session.cjs +2 -2
- package/dist/draft14-session.d.cts +6 -6
- package/dist/draft14-session.d.ts +6 -6
- package/dist/draft14-session.js +1 -1
- package/dist/draft14.cjs +14 -5
- package/dist/draft14.d.cts +14 -12
- package/dist/draft14.d.ts +14 -12
- package/dist/draft14.js +13 -4
- package/dist/draft15-session.cjs +2 -2
- package/dist/draft15-session.d.cts +6 -6
- package/dist/draft15-session.d.ts +6 -6
- package/dist/draft15-session.js +1 -1
- package/dist/draft15.cjs +8 -5
- package/dist/draft15.d.cts +14 -12
- package/dist/draft15.d.ts +14 -12
- package/dist/draft15.js +16 -13
- package/dist/draft16-session.cjs +2 -2
- package/dist/draft16-session.d.cts +6 -6
- package/dist/draft16-session.d.ts +6 -6
- package/dist/draft16-session.js +1 -1
- package/dist/draft16.cjs +8 -5
- package/dist/draft16.d.cts +14 -12
- package/dist/draft16.d.ts +14 -12
- package/dist/draft16.js +16 -13
- package/dist/draft17-session.cjs +2 -2
- package/dist/draft17-session.d.cts +7 -7
- package/dist/draft17-session.d.ts +7 -7
- package/dist/draft17-session.js +1 -1
- package/dist/draft17.cjs +8 -5
- package/dist/draft17.d.cts +11 -9
- package/dist/draft17.d.ts +11 -9
- package/dist/draft17.js +16 -13
- package/dist/draft7-session.cjs +2 -2
- package/dist/draft7-session.d.cts +6 -5
- package/dist/draft7-session.d.ts +6 -5
- package/dist/draft7-session.js +1 -1
- package/dist/draft7.cjs +71 -14
- package/dist/draft7.d.cts +53 -134
- package/dist/draft7.d.ts +53 -134
- package/dist/draft7.js +76 -19
- package/dist/draft8-session.cjs +2 -2
- package/dist/draft8-session.d.cts +6 -6
- package/dist/draft8-session.d.ts +6 -6
- package/dist/draft8-session.js +1 -1
- package/dist/draft8.cjs +8 -5
- package/dist/draft8.d.cts +14 -12
- package/dist/draft8.d.ts +14 -12
- package/dist/draft8.js +7 -4
- package/dist/draft9-session.cjs +2 -2
- package/dist/draft9-session.d.cts +6 -6
- package/dist/draft9-session.d.ts +6 -6
- package/dist/draft9-session.js +1 -1
- package/dist/draft9.cjs +8 -5
- package/dist/draft9.d.cts +14 -12
- package/dist/draft9.d.ts +14 -12
- package/dist/draft9.js +7 -4
- package/dist/index.cjs +52 -74
- package/dist/index.d.cts +41 -60
- package/dist/index.d.ts +41 -60
- package/dist/index.js +40 -62
- package/dist/{session-types-CJIFbTPd.d.ts → session-types-C7UONex5.d.cts} +28 -27
- package/dist/{session-types-Cbq8IGCP.d.cts → session-types-X-IYEGoG.d.ts} +28 -27
- package/dist/session.cjs +46 -13
- package/dist/session.d.cts +28 -9
- package/dist/session.d.ts +28 -9
- package/dist/session.js +46 -13
- package/dist/{types-DxSFvL2b.d.cts → types--Q7_DcvP.d.cts} +43 -39
- package/dist/{types-DxSFvL2b.d.ts → types--Q7_DcvP.d.ts} +43 -39
- package/dist/{types-B0FT5Qs2.d.ts → types-0IWbU9Z_.d.cts} +36 -36
- package/dist/{types-B0FT5Qs2.d.cts → types-0IWbU9Z_.d.ts} +36 -36
- package/dist/{types-gQsr0AAA.d.cts → types-B3g5G9KP.d.cts} +36 -36
- package/dist/{types-gQsr0AAA.d.ts → types-B3g5G9KP.d.ts} +36 -36
- package/dist/{types-Pme7sua0.d.cts → types-BFpFNM_m.d.cts} +57 -32
- package/dist/{types-Pme7sua0.d.ts → types-BFpFNM_m.d.ts} +57 -32
- package/dist/{types-8gUGeMgs.d.ts → types-Br7fm2mf.d.cts} +48 -26
- package/dist/{types-8gUGeMgs.d.cts → types-Br7fm2mf.d.ts} +48 -26
- package/dist/{types-BeAVNU-F.d.cts → types-C2qKTRR_.d.cts} +50 -45
- package/dist/{types-BeAVNU-F.d.ts → types-C2qKTRR_.d.ts} +50 -45
- package/dist/{types-DKpsfD3i.d.cts → types-DEaypVsD.d.cts} +115 -55
- package/dist/{types-DKpsfD3i.d.ts → types-DEaypVsD.d.ts} +115 -55
- package/dist/types-Dl4IaAHR.d.cts +282 -0
- package/dist/types-Dl4IaAHR.d.ts +282 -0
- package/dist/{types-CDa7F5ct.d.cts → types-DrQpTW1G.d.cts} +58 -50
- package/dist/{types-CDa7F5ct.d.ts → types-DrQpTW1G.d.ts} +58 -50
- package/dist/types-DuIO7cWK.d.cts +25 -0
- package/dist/types-DuIO7cWK.d.ts +25 -0
- package/dist/{types-B9A2g05r.d.cts → types-DuxfOYiO.d.cts} +47 -31
- package/dist/{types-B9A2g05r.d.ts → types-DuxfOYiO.d.ts} +47 -31
- package/dist/{types-D1dI_oTX.d.cts → types-jV1yDDBA.d.cts} +36 -36
- package/dist/{types-D1dI_oTX.d.ts → types-jV1yDDBA.d.ts} +36 -36
- package/package.json +192 -192
- package/src/core/buffer-reader.ts +130 -130
- package/src/core/buffer-writer.ts +100 -100
- package/src/core/errors.ts +1 -1
- package/src/core/hex.ts +21 -21
- package/src/core/session-types.ts +61 -59
- package/src/core/types.ts +18 -361
- package/src/drafts/draft07/codec.ts +1058 -938
- package/src/drafts/draft07/data-streams.ts +240 -240
- package/src/drafts/draft07/index.ts +124 -89
- package/src/drafts/draft07/messages.ts +76 -42
- package/src/drafts/draft07/rules.ts +107 -101
- package/src/drafts/draft07/session-fsm.ts +315 -287
- package/src/drafts/draft07/session.ts +8 -6
- package/src/drafts/draft07/types.ts +406 -72
- package/src/drafts/draft07/varint.ts +51 -51
- package/src/drafts/draft08/codec.ts +985 -953
- package/src/drafts/draft08/data-streams.ts +363 -359
- package/src/drafts/draft08/index.ts +10 -9
- package/src/drafts/draft08/messages.ts +59 -59
- package/src/drafts/draft08/rules.ts +73 -73
- package/src/drafts/draft08/session-fsm.ts +335 -303
- package/src/drafts/draft08/session.ts +6 -7
- package/src/drafts/draft08/types.ts +380 -384
- package/src/drafts/draft09/codec.ts +977 -945
- package/src/drafts/draft09/data-streams.ts +336 -332
- package/src/drafts/draft09/index.ts +10 -9
- package/src/drafts/draft09/messages.ts +59 -59
- package/src/drafts/draft09/rules.ts +73 -73
- package/src/drafts/draft09/session-fsm.ts +335 -303
- package/src/drafts/draft09/session.ts +6 -7
- package/src/drafts/draft09/types.ts +380 -384
- package/src/drafts/draft10/codec.ts +977 -945
- package/src/drafts/draft10/data-streams.ts +336 -332
- package/src/drafts/draft10/index.ts +10 -9
- package/src/drafts/draft10/messages.ts +59 -59
- package/src/drafts/draft10/rules.ts +73 -73
- package/src/drafts/draft10/session-fsm.ts +335 -303
- package/src/drafts/draft10/session.ts +6 -7
- package/src/drafts/draft10/types.ts +380 -384
- package/src/drafts/draft11/codec.ts +1054 -988
- package/src/drafts/draft11/data-streams.ts +316 -269
- package/src/drafts/draft11/index.ts +10 -9
- package/src/drafts/draft11/messages.ts +60 -59
- package/src/drafts/draft11/rules.ts +73 -73
- package/src/drafts/draft11/session-fsm.ts +359 -333
- package/src/drafts/draft11/session.ts +6 -7
- package/src/drafts/draft11/types.ts +385 -381
- package/src/drafts/draft12/codec.ts +1178 -1122
- package/src/drafts/draft12/data-streams.ts +324 -275
- package/src/drafts/draft12/index.ts +10 -9
- package/src/drafts/draft12/messages.ts +67 -66
- package/src/drafts/draft12/rules.ts +79 -79
- package/src/drafts/draft12/session-fsm.ts +384 -358
- package/src/drafts/draft12/session.ts +6 -7
- package/src/drafts/draft12/types.ts +424 -419
- package/src/drafts/draft13/codec.ts +1296 -1206
- package/src/drafts/draft13/data-streams.ts +326 -275
- package/src/drafts/draft13/index.ts +10 -9
- package/src/drafts/draft13/messages.ts +73 -68
- package/src/drafts/draft13/rules.ts +81 -81
- package/src/drafts/draft13/session-fsm.ts +392 -366
- package/src/drafts/draft13/session.ts +6 -7
- package/src/drafts/draft13/types.ts +447 -438
- package/src/drafts/draft14/codec.ts +1347 -1043
- package/src/drafts/draft14/data-streams.ts +831 -813
- package/src/drafts/draft14/index.ts +17 -9
- package/src/drafts/draft14/messages.ts +65 -65
- package/src/drafts/draft14/rules.ts +81 -81
- package/src/drafts/draft14/session-fsm.ts +415 -405
- package/src/drafts/draft14/session.ts +6 -7
- package/src/drafts/draft14/types.ts +451 -381
- package/src/drafts/draft15/codec.ts +1138 -978
- package/src/drafts/draft15/data-streams.ts +842 -794
- package/src/drafts/draft15/index.ts +10 -9
- package/src/drafts/draft15/messages.ts +54 -53
- package/src/drafts/draft15/rules.ts +67 -67
- package/src/drafts/draft15/session-fsm.ts +319 -299
- package/src/drafts/draft15/session.ts +6 -7
- package/src/drafts/draft15/types.ts +357 -339
- package/src/drafts/draft16/codec.ts +1242 -966
- package/src/drafts/draft16/data-streams.ts +835 -789
- package/src/drafts/draft16/index.ts +10 -9
- package/src/drafts/draft16/messages.ts +56 -55
- package/src/drafts/draft16/rules.ts +69 -69
- package/src/drafts/draft16/session-fsm.ts +319 -299
- package/src/drafts/draft16/session.ts +6 -7
- package/src/drafts/draft16/types.ts +386 -357
- package/src/drafts/draft17/codec.ts +1209 -971
- package/src/drafts/draft17/data-streams.ts +969 -758
- package/src/drafts/draft17/index.ts +9 -7
- package/src/drafts/draft17/messages.ts +43 -42
- package/src/drafts/draft17/rules.ts +58 -58
- package/src/drafts/draft17/session-fsm.ts +248 -216
- package/src/drafts/draft17/session.ts +5 -5
- package/src/drafts/draft17/types.ts +337 -313
- package/src/index.ts +85 -274
- package/src/session.ts +62 -23
- package/dist/chunk-FZDIEHQL.cjs +0 -1042
- package/dist/chunk-HDWJ2GDE.js +0 -24
- package/dist/chunk-KNDYS3DK.cjs +0 -24
- package/dist/chunk-N2KBF5Q2.js +0 -1042
- package/dist/types-QNXoxC9Y.d.cts +0 -236
- package/dist/types-QNXoxC9Y.d.ts +0 -236
- package/src/drafts/draft07/announce-fsm.ts +0 -2
- package/src/drafts/draft07/parameters.ts +0 -12
- package/src/drafts/draft07/subscription-fsm.ts +0 -3
|
@@ -7,84 +7,84 @@ import type {
|
|
|
7
7
|
SubscriptionState,
|
|
8
8
|
TransitionResult,
|
|
9
9
|
ValidationResult,
|
|
10
|
-
} from
|
|
10
|
+
} from '../../core/session-types.js'
|
|
11
11
|
import {
|
|
12
12
|
CLIENT_ONLY_MESSAGES,
|
|
13
13
|
getLegalIncoming,
|
|
14
14
|
getLegalOutgoing,
|
|
15
15
|
SERVER_ONLY_MESSAGES,
|
|
16
|
-
} from
|
|
17
|
-
import type { Draft08Message, Draft08MessageType } from
|
|
16
|
+
} from './rules.js'
|
|
17
|
+
import type { Draft08Message, Draft08MessageType } from './types.js'
|
|
18
18
|
|
|
19
19
|
function violation(
|
|
20
|
-
code: ProtocolViolation<Draft08MessageType>[
|
|
20
|
+
code: ProtocolViolation<Draft08MessageType>['code'],
|
|
21
21
|
message: string,
|
|
22
22
|
currentPhase: SessionPhase,
|
|
23
23
|
offendingMessage: Draft08MessageType,
|
|
24
24
|
): ProtocolViolation<Draft08MessageType> {
|
|
25
|
-
return { code, message, currentPhase, offendingMessage }
|
|
25
|
+
return { code, message, currentPhase, offendingMessage }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export class Draft08SessionFSM {
|
|
29
|
-
private _phase: SessionPhase =
|
|
30
|
-
private _role:
|
|
31
|
-
private _subscriptions = new Map<bigint, SubscriptionState>()
|
|
32
|
-
private _announces = new Map<string, AnnounceState>()
|
|
33
|
-
private _fetches = new Map<bigint, FetchState>()
|
|
34
|
-
private _subscribeIds = new Set<bigint>()
|
|
29
|
+
private _phase: SessionPhase = 'idle'
|
|
30
|
+
private _role: 'client' | 'server'
|
|
31
|
+
private _subscriptions = new Map<bigint, SubscriptionState>()
|
|
32
|
+
private _announces = new Map<string, AnnounceState>()
|
|
33
|
+
private _fetches = new Map<bigint, FetchState>()
|
|
34
|
+
private _subscribeIds = new Set<bigint>()
|
|
35
35
|
|
|
36
|
-
constructor(role:
|
|
37
|
-
this._role = role
|
|
36
|
+
constructor(role: 'client' | 'server') {
|
|
37
|
+
this._role = role
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
get phase(): SessionPhase {
|
|
41
|
-
return this._phase
|
|
41
|
+
return this._phase
|
|
42
42
|
}
|
|
43
|
-
get role():
|
|
44
|
-
return this._role
|
|
43
|
+
get role(): 'client' | 'server' {
|
|
44
|
+
return this._role
|
|
45
45
|
}
|
|
46
46
|
get subscriptions(): ReadonlyMap<bigint, SubscriptionState> {
|
|
47
|
-
return this._subscriptions
|
|
47
|
+
return this._subscriptions
|
|
48
48
|
}
|
|
49
49
|
get announces(): ReadonlyMap<string, AnnounceState> {
|
|
50
|
-
return this._announces
|
|
50
|
+
return this._announces
|
|
51
51
|
}
|
|
52
52
|
get fetches(): ReadonlyMap<bigint, FetchState> {
|
|
53
|
-
return this._fetches
|
|
53
|
+
return this._fetches
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
get legalOutgoing(): ReadonlySet<Draft08MessageType> {
|
|
57
|
-
return getLegalOutgoing(this._phase, this._role)
|
|
57
|
+
return getLegalOutgoing(this._phase, this._role)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
get legalIncoming(): ReadonlySet<Draft08MessageType> {
|
|
61
|
-
return getLegalIncoming(this._phase, this._role)
|
|
61
|
+
return getLegalIncoming(this._phase, this._role)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
private checkRole(
|
|
65
65
|
message: Draft08Message,
|
|
66
|
-
direction:
|
|
66
|
+
direction: 'inbound' | 'outbound',
|
|
67
67
|
): ProtocolViolation<Draft08MessageType> | null {
|
|
68
68
|
const senderRole =
|
|
69
|
-
direction ===
|
|
69
|
+
direction === 'outbound' ? this._role : this._role === 'client' ? 'server' : 'client'
|
|
70
70
|
|
|
71
|
-
if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !==
|
|
71
|
+
if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== 'client') {
|
|
72
72
|
return violation(
|
|
73
|
-
|
|
73
|
+
'ROLE_VIOLATION',
|
|
74
74
|
`${message.type} can only be sent by client`,
|
|
75
75
|
this._phase,
|
|
76
76
|
message.type,
|
|
77
|
-
)
|
|
77
|
+
)
|
|
78
78
|
}
|
|
79
|
-
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !==
|
|
79
|
+
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== 'server') {
|
|
80
80
|
return violation(
|
|
81
|
-
|
|
81
|
+
'ROLE_VIOLATION',
|
|
82
82
|
`${message.type} can only be sent by server`,
|
|
83
83
|
this._phase,
|
|
84
84
|
message.type,
|
|
85
|
-
)
|
|
85
|
+
)
|
|
86
86
|
}
|
|
87
|
-
return null
|
|
87
|
+
return null
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
private checkDuplicateSubscribeId(
|
|
@@ -93,13 +93,13 @@ export class Draft08SessionFSM {
|
|
|
93
93
|
): ProtocolViolation<Draft08MessageType> | null {
|
|
94
94
|
if (this._subscribeIds.has(subscribeId)) {
|
|
95
95
|
return violation(
|
|
96
|
-
|
|
96
|
+
'DUPLICATE_REQUEST_ID',
|
|
97
97
|
`Subscribe ID ${subscribeId} already in use`,
|
|
98
98
|
this._phase,
|
|
99
99
|
msgType,
|
|
100
|
-
)
|
|
100
|
+
)
|
|
101
101
|
}
|
|
102
|
-
return null
|
|
102
|
+
return null
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
private checkKnownSubscribeId(
|
|
@@ -108,192 +108,199 @@ export class Draft08SessionFSM {
|
|
|
108
108
|
): ProtocolViolation<Draft08MessageType> | null {
|
|
109
109
|
if (!this._subscribeIds.has(subscribeId)) {
|
|
110
110
|
return violation(
|
|
111
|
-
|
|
111
|
+
'UNKNOWN_REQUEST_ID',
|
|
112
112
|
`No request with subscribe ID ${subscribeId}`,
|
|
113
113
|
this._phase,
|
|
114
114
|
msgType,
|
|
115
|
-
)
|
|
115
|
+
)
|
|
116
116
|
}
|
|
117
|
-
return null
|
|
117
|
+
return null
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
validateOutgoing(message: Draft08Message): ValidationResult<Draft08MessageType> {
|
|
121
|
-
const roleViolation = this.checkRole(message,
|
|
122
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
121
|
+
const roleViolation = this.checkRole(message, 'outbound')
|
|
122
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
123
123
|
|
|
124
124
|
if (!this.legalOutgoing.has(message.type)) {
|
|
125
125
|
return {
|
|
126
126
|
ok: false,
|
|
127
127
|
violation: violation(
|
|
128
|
-
this._phase ===
|
|
129
|
-
?
|
|
130
|
-
:
|
|
128
|
+
this._phase === 'idle' || this._phase === 'setup'
|
|
129
|
+
? 'MESSAGE_BEFORE_SETUP'
|
|
130
|
+
: 'UNEXPECTED_MESSAGE',
|
|
131
131
|
`Cannot send ${message.type} in phase ${this._phase}`,
|
|
132
132
|
this._phase,
|
|
133
133
|
message.type,
|
|
134
134
|
),
|
|
135
|
-
}
|
|
135
|
+
}
|
|
136
136
|
}
|
|
137
|
-
return { ok: true }
|
|
137
|
+
return { ok: true }
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
receive(message: Draft08Message): TransitionResult<Draft08MessageType> {
|
|
141
|
-
const roleViolation = this.checkRole(message,
|
|
142
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
143
|
-
return this.applyTransition(message,
|
|
141
|
+
const roleViolation = this.checkRole(message, 'inbound')
|
|
142
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
143
|
+
return this.applyTransition(message, 'inbound')
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
send(message: Draft08Message): TransitionResult<Draft08MessageType> {
|
|
147
|
-
const roleViolation = this.checkRole(message,
|
|
148
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
149
|
-
return this.applyTransition(message,
|
|
147
|
+
const roleViolation = this.checkRole(message, 'outbound')
|
|
148
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
149
|
+
return this.applyTransition(message, 'outbound')
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
private applyTransition(
|
|
153
153
|
message: Draft08Message,
|
|
154
|
-
direction:
|
|
154
|
+
direction: 'inbound' | 'outbound',
|
|
155
155
|
): TransitionResult<Draft08MessageType> {
|
|
156
|
-
const sideEffects: SideEffect[] = []
|
|
156
|
+
const sideEffects: SideEffect[] = []
|
|
157
157
|
|
|
158
158
|
switch (message.type) {
|
|
159
|
-
case
|
|
160
|
-
return this.handleClientSetup(direction)
|
|
161
|
-
case
|
|
162
|
-
return this.handleServerSetup(direction)
|
|
163
|
-
case
|
|
164
|
-
return this.handleGoAway(message, sideEffects)
|
|
159
|
+
case 'client_setup':
|
|
160
|
+
return this.handleClientSetup(direction)
|
|
161
|
+
case 'server_setup':
|
|
162
|
+
return this.handleServerSetup(direction)
|
|
163
|
+
case 'goaway':
|
|
164
|
+
return this.handleGoAway(message, sideEffects)
|
|
165
165
|
|
|
166
166
|
// Subscribe lifecycle
|
|
167
|
-
case
|
|
168
|
-
return this.handleSubscribe(message, sideEffects)
|
|
169
|
-
case
|
|
170
|
-
return this.handleSubscribeOk(message, sideEffects)
|
|
171
|
-
case
|
|
172
|
-
return this.handleSubscribeError(message, sideEffects)
|
|
173
|
-
case
|
|
174
|
-
return this.handleSubscribeUpdate(message, sideEffects)
|
|
175
|
-
case
|
|
176
|
-
return this.handleSubscribeDone(message, sideEffects)
|
|
177
|
-
case
|
|
178
|
-
return this.handleUnsubscribe(message, sideEffects)
|
|
167
|
+
case 'subscribe':
|
|
168
|
+
return this.handleSubscribe(message, sideEffects)
|
|
169
|
+
case 'subscribe_ok':
|
|
170
|
+
return this.handleSubscribeOk(message, sideEffects)
|
|
171
|
+
case 'subscribe_error':
|
|
172
|
+
return this.handleSubscribeError(message, sideEffects)
|
|
173
|
+
case 'subscribe_update':
|
|
174
|
+
return this.handleSubscribeUpdate(message, sideEffects)
|
|
175
|
+
case 'subscribe_done':
|
|
176
|
+
return this.handleSubscribeDone(message, sideEffects)
|
|
177
|
+
case 'unsubscribe':
|
|
178
|
+
return this.handleUnsubscribe(message, sideEffects)
|
|
179
179
|
|
|
180
180
|
// Announce lifecycle
|
|
181
|
-
case
|
|
182
|
-
return this.handleAnnounce(message, sideEffects)
|
|
183
|
-
case
|
|
184
|
-
return this.handleAnnounceOk(message, sideEffects)
|
|
185
|
-
case
|
|
186
|
-
return this.handleAnnounceError(message, sideEffects)
|
|
187
|
-
case
|
|
188
|
-
return this.handleUnannounce(message, sideEffects)
|
|
189
|
-
case
|
|
190
|
-
return this.handleAnnounceCancel(message, sideEffects)
|
|
181
|
+
case 'announce':
|
|
182
|
+
return this.handleAnnounce(message, sideEffects)
|
|
183
|
+
case 'announce_ok':
|
|
184
|
+
return this.handleAnnounceOk(message, sideEffects)
|
|
185
|
+
case 'announce_error':
|
|
186
|
+
return this.handleAnnounceError(message, sideEffects)
|
|
187
|
+
case 'unannounce':
|
|
188
|
+
return this.handleUnannounce(message, sideEffects)
|
|
189
|
+
case 'announce_cancel':
|
|
190
|
+
return this.handleAnnounceCancel(message, sideEffects)
|
|
191
191
|
|
|
192
192
|
// Fetch lifecycle
|
|
193
|
-
case
|
|
194
|
-
return this.handleFetch(message, sideEffects)
|
|
195
|
-
case
|
|
196
|
-
return this.handleFetchOk(message, sideEffects)
|
|
197
|
-
case
|
|
198
|
-
return this.handleFetchError(message, sideEffects)
|
|
199
|
-
case
|
|
200
|
-
return this.handleFetchCancel(message, sideEffects)
|
|
193
|
+
case 'fetch':
|
|
194
|
+
return this.handleFetch(message, sideEffects)
|
|
195
|
+
case 'fetch_ok':
|
|
196
|
+
return this.handleFetchOk(message, sideEffects)
|
|
197
|
+
case 'fetch_error':
|
|
198
|
+
return this.handleFetchError(message, sideEffects)
|
|
199
|
+
case 'fetch_cancel':
|
|
200
|
+
return this.handleFetchCancel(message, sideEffects)
|
|
201
201
|
default:
|
|
202
|
-
return this.handleReadyPhaseMessage(message)
|
|
202
|
+
return this.handleReadyPhaseMessage(message)
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
private handleClientSetup(
|
|
207
|
-
direction:
|
|
207
|
+
direction: 'inbound' | 'outbound',
|
|
208
208
|
): TransitionResult<Draft08MessageType> {
|
|
209
|
-
if (this._phase !==
|
|
209
|
+
if (this._phase !== 'idle') {
|
|
210
210
|
return {
|
|
211
211
|
ok: false,
|
|
212
212
|
violation: violation(
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
'SETUP_VIOLATION',
|
|
214
|
+
'CLIENT_SETUP already sent/received',
|
|
215
215
|
this._phase,
|
|
216
|
-
|
|
216
|
+
'client_setup',
|
|
217
217
|
),
|
|
218
|
-
}
|
|
218
|
+
}
|
|
219
219
|
}
|
|
220
|
-
if (direction ===
|
|
220
|
+
if (direction === 'outbound' && this._role !== 'client') {
|
|
221
221
|
return {
|
|
222
222
|
ok: false,
|
|
223
223
|
violation: violation(
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
'ROLE_VIOLATION',
|
|
225
|
+
'Only client can send CLIENT_SETUP',
|
|
226
226
|
this._phase,
|
|
227
|
-
|
|
227
|
+
'client_setup',
|
|
228
228
|
),
|
|
229
|
-
}
|
|
229
|
+
}
|
|
230
230
|
}
|
|
231
|
-
this._phase =
|
|
232
|
-
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
231
|
+
this._phase = 'setup'
|
|
232
|
+
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
233
233
|
}
|
|
234
234
|
|
|
235
235
|
private handleServerSetup(
|
|
236
|
-
direction:
|
|
236
|
+
direction: 'inbound' | 'outbound',
|
|
237
237
|
): TransitionResult<Draft08MessageType> {
|
|
238
|
-
if (this._phase !==
|
|
238
|
+
if (this._phase !== 'setup') {
|
|
239
239
|
return {
|
|
240
240
|
ok: false,
|
|
241
241
|
violation: violation(
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
'SETUP_VIOLATION',
|
|
243
|
+
'SERVER_SETUP before CLIENT_SETUP',
|
|
244
244
|
this._phase,
|
|
245
|
-
|
|
245
|
+
'server_setup',
|
|
246
246
|
),
|
|
247
|
-
}
|
|
247
|
+
}
|
|
248
248
|
}
|
|
249
|
-
if (direction ===
|
|
249
|
+
if (direction === 'outbound' && this._role !== 'server') {
|
|
250
250
|
return {
|
|
251
251
|
ok: false,
|
|
252
252
|
violation: violation(
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
'ROLE_VIOLATION',
|
|
254
|
+
'Only server can send SERVER_SETUP',
|
|
255
255
|
this._phase,
|
|
256
|
-
|
|
256
|
+
'server_setup',
|
|
257
257
|
),
|
|
258
|
-
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
this._phase = 'ready'
|
|
261
|
+
return {
|
|
262
|
+
ok: true,
|
|
263
|
+
phase: this._phase,
|
|
264
|
+
sideEffects: [{ type: 'session-ready' }],
|
|
259
265
|
}
|
|
260
|
-
this._phase = "ready";
|
|
261
|
-
return { ok: true, phase: this._phase, sideEffects: [{ type: "session-ready" }] };
|
|
262
266
|
}
|
|
263
267
|
|
|
264
268
|
private handleGoAway(
|
|
265
269
|
message: Draft08Message,
|
|
266
270
|
sideEffects: SideEffect[],
|
|
267
271
|
): TransitionResult<Draft08MessageType> {
|
|
268
|
-
if (this._phase !==
|
|
272
|
+
if (this._phase !== 'ready' && this._phase !== 'draining') {
|
|
269
273
|
return {
|
|
270
274
|
ok: false,
|
|
271
275
|
violation: violation(
|
|
272
|
-
|
|
276
|
+
'UNEXPECTED_MESSAGE',
|
|
273
277
|
`GOAWAY not valid in phase ${this._phase}`,
|
|
274
278
|
this._phase,
|
|
275
|
-
|
|
279
|
+
'goaway',
|
|
276
280
|
),
|
|
277
|
-
}
|
|
281
|
+
}
|
|
278
282
|
}
|
|
279
|
-
this._phase =
|
|
280
|
-
const goaway = message as import(
|
|
281
|
-
sideEffects.push({
|
|
282
|
-
|
|
283
|
+
this._phase = 'draining'
|
|
284
|
+
const goaway = message as import('./types.js').Draft08GoAway
|
|
285
|
+
sideEffects.push({
|
|
286
|
+
type: 'session-draining',
|
|
287
|
+
goAwayUri: goaway.new_session_uri,
|
|
288
|
+
})
|
|
289
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
283
290
|
}
|
|
284
291
|
|
|
285
292
|
private requireReady(msgType: Draft08MessageType): ProtocolViolation<Draft08MessageType> | null {
|
|
286
|
-
if (this._phase !==
|
|
293
|
+
if (this._phase !== 'ready' && this._phase !== 'draining') {
|
|
287
294
|
return violation(
|
|
288
|
-
this._phase ===
|
|
289
|
-
?
|
|
290
|
-
:
|
|
295
|
+
this._phase === 'idle' || this._phase === 'setup'
|
|
296
|
+
? 'MESSAGE_BEFORE_SETUP'
|
|
297
|
+
: 'UNEXPECTED_MESSAGE',
|
|
291
298
|
`${msgType} requires ready phase, current: ${this._phase}`,
|
|
292
299
|
this._phase,
|
|
293
300
|
msgType,
|
|
294
|
-
)
|
|
301
|
+
)
|
|
295
302
|
}
|
|
296
|
-
return null
|
|
303
|
+
return null
|
|
297
304
|
}
|
|
298
305
|
|
|
299
306
|
// ─── Subscribe lifecycle ───
|
|
@@ -302,184 +309,190 @@ export class Draft08SessionFSM {
|
|
|
302
309
|
message: Draft08Message,
|
|
303
310
|
sideEffects: SideEffect[],
|
|
304
311
|
): TransitionResult<Draft08MessageType> {
|
|
305
|
-
const err = this.requireReady(message.type)
|
|
306
|
-
if (err) return { ok: false, violation: err }
|
|
307
|
-
const sub = message as import(
|
|
308
|
-
const dupErr = this.checkDuplicateSubscribeId(sub.subscribe_id, message.type)
|
|
309
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
310
|
-
this._subscribeIds.add(sub.subscribe_id)
|
|
312
|
+
const err = this.requireReady(message.type)
|
|
313
|
+
if (err) return { ok: false, violation: err }
|
|
314
|
+
const sub = message as import('./types.js').Draft08Subscribe
|
|
315
|
+
const dupErr = this.checkDuplicateSubscribeId(sub.subscribe_id, message.type)
|
|
316
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
317
|
+
this._subscribeIds.add(sub.subscribe_id)
|
|
311
318
|
this._subscriptions.set(sub.subscribe_id, {
|
|
312
319
|
subscribeId: sub.subscribe_id,
|
|
313
|
-
phase:
|
|
320
|
+
phase: 'pending',
|
|
314
321
|
trackNamespace: sub.track_namespace,
|
|
315
322
|
trackName: sub.track_name,
|
|
316
|
-
})
|
|
317
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
323
|
+
})
|
|
324
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
318
325
|
}
|
|
319
326
|
|
|
320
327
|
private handleSubscribeOk(
|
|
321
328
|
message: Draft08Message,
|
|
322
329
|
sideEffects: SideEffect[],
|
|
323
330
|
): TransitionResult<Draft08MessageType> {
|
|
324
|
-
const err = this.requireReady(message.type)
|
|
325
|
-
if (err) return { ok: false, violation: err }
|
|
326
|
-
const ok = message as import(
|
|
327
|
-
const idErr = this.checkKnownSubscribeId(ok.subscribe_id, message.type)
|
|
328
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
329
|
-
const existing = this._subscriptions.get(ok.subscribe_id)
|
|
331
|
+
const err = this.requireReady(message.type)
|
|
332
|
+
if (err) return { ok: false, violation: err }
|
|
333
|
+
const ok = message as import('./types.js').Draft08SubscribeOk
|
|
334
|
+
const idErr = this.checkKnownSubscribeId(ok.subscribe_id, message.type)
|
|
335
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
336
|
+
const existing = this._subscriptions.get(ok.subscribe_id)
|
|
330
337
|
if (!existing)
|
|
331
338
|
return {
|
|
332
339
|
ok: false,
|
|
333
340
|
violation: violation(
|
|
334
|
-
|
|
341
|
+
'UNKNOWN_REQUEST_ID',
|
|
335
342
|
`No subscription with subscribe ID ${ok.subscribe_id}`,
|
|
336
343
|
this._phase,
|
|
337
344
|
message.type,
|
|
338
345
|
),
|
|
339
|
-
}
|
|
340
|
-
if (existing.phase !==
|
|
346
|
+
}
|
|
347
|
+
if (existing.phase !== 'pending')
|
|
341
348
|
return {
|
|
342
349
|
ok: false,
|
|
343
350
|
violation: violation(
|
|
344
|
-
|
|
351
|
+
'STATE_VIOLATION',
|
|
345
352
|
`Subscription ${ok.subscribe_id} is ${existing.phase}, not pending`,
|
|
346
353
|
this._phase,
|
|
347
354
|
message.type,
|
|
348
355
|
),
|
|
349
|
-
}
|
|
350
|
-
this._subscriptions.set(ok.subscribe_id, { ...existing, phase:
|
|
351
|
-
sideEffects.push({
|
|
352
|
-
|
|
356
|
+
}
|
|
357
|
+
this._subscriptions.set(ok.subscribe_id, { ...existing, phase: 'active' })
|
|
358
|
+
sideEffects.push({
|
|
359
|
+
type: 'subscription-activated',
|
|
360
|
+
subscribeId: ok.subscribe_id,
|
|
361
|
+
})
|
|
362
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
353
363
|
}
|
|
354
364
|
|
|
355
365
|
private handleSubscribeError(
|
|
356
366
|
message: Draft08Message,
|
|
357
367
|
sideEffects: SideEffect[],
|
|
358
368
|
): TransitionResult<Draft08MessageType> {
|
|
359
|
-
const err = this.requireReady(message.type)
|
|
360
|
-
if (err) return { ok: false, violation: err }
|
|
361
|
-
const subErr = message as import(
|
|
362
|
-
const idErr = this.checkKnownSubscribeId(subErr.subscribe_id, message.type)
|
|
363
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
364
|
-
const existing = this._subscriptions.get(subErr.subscribe_id)
|
|
369
|
+
const err = this.requireReady(message.type)
|
|
370
|
+
if (err) return { ok: false, violation: err }
|
|
371
|
+
const subErr = message as import('./types.js').Draft08SubscribeError
|
|
372
|
+
const idErr = this.checkKnownSubscribeId(subErr.subscribe_id, message.type)
|
|
373
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
374
|
+
const existing = this._subscriptions.get(subErr.subscribe_id)
|
|
365
375
|
if (!existing)
|
|
366
376
|
return {
|
|
367
377
|
ok: false,
|
|
368
378
|
violation: violation(
|
|
369
|
-
|
|
379
|
+
'UNKNOWN_REQUEST_ID',
|
|
370
380
|
`No subscription with subscribe ID ${subErr.subscribe_id}`,
|
|
371
381
|
this._phase,
|
|
372
382
|
message.type,
|
|
373
383
|
),
|
|
374
|
-
}
|
|
375
|
-
if (existing.phase !==
|
|
384
|
+
}
|
|
385
|
+
if (existing.phase !== 'pending')
|
|
376
386
|
return {
|
|
377
387
|
ok: false,
|
|
378
388
|
violation: violation(
|
|
379
|
-
|
|
389
|
+
'STATE_VIOLATION',
|
|
380
390
|
`Subscription ${subErr.subscribe_id} is ${existing.phase}, not pending`,
|
|
381
391
|
this._phase,
|
|
382
392
|
message.type,
|
|
383
393
|
),
|
|
384
|
-
}
|
|
385
|
-
this._subscriptions.set(subErr.subscribe_id, {
|
|
394
|
+
}
|
|
395
|
+
this._subscriptions.set(subErr.subscribe_id, {
|
|
396
|
+
...existing,
|
|
397
|
+
phase: 'error',
|
|
398
|
+
})
|
|
386
399
|
sideEffects.push({
|
|
387
|
-
type:
|
|
400
|
+
type: 'subscription-ended',
|
|
388
401
|
subscribeId: subErr.subscribe_id,
|
|
389
402
|
reason: subErr.reason_phrase,
|
|
390
|
-
})
|
|
391
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
403
|
+
})
|
|
404
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
392
405
|
}
|
|
393
406
|
|
|
394
407
|
private handleSubscribeUpdate(
|
|
395
408
|
message: Draft08Message,
|
|
396
409
|
sideEffects: SideEffect[],
|
|
397
410
|
): TransitionResult<Draft08MessageType> {
|
|
398
|
-
const err = this.requireReady(message.type)
|
|
399
|
-
if (err) return { ok: false, violation: err }
|
|
400
|
-
const update = message as import(
|
|
401
|
-
const idErr = this.checkKnownSubscribeId(update.subscribe_id, message.type)
|
|
402
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
403
|
-
const existing = this._subscriptions.get(update.subscribe_id)
|
|
411
|
+
const err = this.requireReady(message.type)
|
|
412
|
+
if (err) return { ok: false, violation: err }
|
|
413
|
+
const update = message as import('./types.js').Draft08SubscribeUpdate
|
|
414
|
+
const idErr = this.checkKnownSubscribeId(update.subscribe_id, message.type)
|
|
415
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
416
|
+
const existing = this._subscriptions.get(update.subscribe_id)
|
|
404
417
|
if (!existing)
|
|
405
418
|
return {
|
|
406
419
|
ok: false,
|
|
407
420
|
violation: violation(
|
|
408
|
-
|
|
421
|
+
'UNKNOWN_REQUEST_ID',
|
|
409
422
|
`No subscription with subscribe ID ${update.subscribe_id}`,
|
|
410
423
|
this._phase,
|
|
411
424
|
message.type,
|
|
412
425
|
),
|
|
413
|
-
}
|
|
414
|
-
if (existing.phase !==
|
|
426
|
+
}
|
|
427
|
+
if (existing.phase !== 'active')
|
|
415
428
|
return {
|
|
416
429
|
ok: false,
|
|
417
430
|
violation: violation(
|
|
418
|
-
|
|
431
|
+
'STATE_VIOLATION',
|
|
419
432
|
`Subscription ${update.subscribe_id} is ${existing.phase}, not active`,
|
|
420
433
|
this._phase,
|
|
421
434
|
message.type,
|
|
422
435
|
),
|
|
423
|
-
}
|
|
424
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
436
|
+
}
|
|
437
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
425
438
|
}
|
|
426
439
|
|
|
427
440
|
private handleSubscribeDone(
|
|
428
441
|
message: Draft08Message,
|
|
429
442
|
sideEffects: SideEffect[],
|
|
430
443
|
): TransitionResult<Draft08MessageType> {
|
|
431
|
-
const err = this.requireReady(message.type)
|
|
432
|
-
if (err) return { ok: false, violation: err }
|
|
433
|
-
const done = message as import(
|
|
434
|
-
const idErr = this.checkKnownSubscribeId(done.subscribe_id, message.type)
|
|
435
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
436
|
-
const existing = this._subscriptions.get(done.subscribe_id)
|
|
444
|
+
const err = this.requireReady(message.type)
|
|
445
|
+
if (err) return { ok: false, violation: err }
|
|
446
|
+
const done = message as import('./types.js').Draft08SubscribeDone
|
|
447
|
+
const idErr = this.checkKnownSubscribeId(done.subscribe_id, message.type)
|
|
448
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
449
|
+
const existing = this._subscriptions.get(done.subscribe_id)
|
|
437
450
|
if (!existing)
|
|
438
451
|
return {
|
|
439
452
|
ok: false,
|
|
440
453
|
violation: violation(
|
|
441
|
-
|
|
454
|
+
'UNKNOWN_REQUEST_ID',
|
|
442
455
|
`No subscription with subscribe ID ${done.subscribe_id}`,
|
|
443
456
|
this._phase,
|
|
444
457
|
message.type,
|
|
445
458
|
),
|
|
446
|
-
}
|
|
447
|
-
this._subscriptions.set(done.subscribe_id, { ...existing, phase:
|
|
459
|
+
}
|
|
460
|
+
this._subscriptions.set(done.subscribe_id, { ...existing, phase: 'done' })
|
|
448
461
|
sideEffects.push({
|
|
449
|
-
type:
|
|
462
|
+
type: 'subscription-ended',
|
|
450
463
|
subscribeId: done.subscribe_id,
|
|
451
464
|
reason: done.reason_phrase,
|
|
452
|
-
})
|
|
453
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
465
|
+
})
|
|
466
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
454
467
|
}
|
|
455
468
|
|
|
456
469
|
private handleUnsubscribe(
|
|
457
470
|
message: Draft08Message,
|
|
458
471
|
sideEffects: SideEffect[],
|
|
459
472
|
): TransitionResult<Draft08MessageType> {
|
|
460
|
-
const err = this.requireReady(message.type)
|
|
461
|
-
if (err) return { ok: false, violation: err }
|
|
462
|
-
const unsub = message as import(
|
|
463
|
-
const idErr = this.checkKnownSubscribeId(unsub.subscribe_id, message.type)
|
|
464
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
465
|
-
const existing = this._subscriptions.get(unsub.subscribe_id)
|
|
473
|
+
const err = this.requireReady(message.type)
|
|
474
|
+
if (err) return { ok: false, violation: err }
|
|
475
|
+
const unsub = message as import('./types.js').Draft08Unsubscribe
|
|
476
|
+
const idErr = this.checkKnownSubscribeId(unsub.subscribe_id, message.type)
|
|
477
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
478
|
+
const existing = this._subscriptions.get(unsub.subscribe_id)
|
|
466
479
|
if (!existing)
|
|
467
480
|
return {
|
|
468
481
|
ok: false,
|
|
469
482
|
violation: violation(
|
|
470
|
-
|
|
483
|
+
'UNKNOWN_REQUEST_ID',
|
|
471
484
|
`No subscription with subscribe ID ${unsub.subscribe_id}`,
|
|
472
485
|
this._phase,
|
|
473
486
|
message.type,
|
|
474
487
|
),
|
|
475
|
-
}
|
|
476
|
-
this._subscriptions.set(unsub.subscribe_id, { ...existing, phase:
|
|
488
|
+
}
|
|
489
|
+
this._subscriptions.set(unsub.subscribe_id, { ...existing, phase: 'done' })
|
|
477
490
|
sideEffects.push({
|
|
478
|
-
type:
|
|
491
|
+
type: 'subscription-ended',
|
|
479
492
|
subscribeId: unsub.subscribe_id,
|
|
480
|
-
reason:
|
|
481
|
-
})
|
|
482
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
493
|
+
reason: 'unsubscribed',
|
|
494
|
+
})
|
|
495
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
483
496
|
}
|
|
484
497
|
|
|
485
498
|
// ─── Announce lifecycle ───
|
|
@@ -488,111 +501,123 @@ export class Draft08SessionFSM {
|
|
|
488
501
|
message: Draft08Message,
|
|
489
502
|
sideEffects: SideEffect[],
|
|
490
503
|
): TransitionResult<Draft08MessageType> {
|
|
491
|
-
const err = this.requireReady(message.type)
|
|
492
|
-
if (err) return { ok: false, violation: err }
|
|
493
|
-
const ann = message as import(
|
|
494
|
-
const nsKey = ann.track_namespace.join(
|
|
495
|
-
this._announces.set(nsKey, {
|
|
496
|
-
|
|
504
|
+
const err = this.requireReady(message.type)
|
|
505
|
+
if (err) return { ok: false, violation: err }
|
|
506
|
+
const ann = message as import('./types.js').Draft08Announce
|
|
507
|
+
const nsKey = ann.track_namespace.join('/')
|
|
508
|
+
this._announces.set(nsKey, {
|
|
509
|
+
namespace: ann.track_namespace,
|
|
510
|
+
phase: 'pending',
|
|
511
|
+
})
|
|
512
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
497
513
|
}
|
|
498
514
|
|
|
499
515
|
private handleAnnounceOk(
|
|
500
516
|
message: Draft08Message,
|
|
501
517
|
sideEffects: SideEffect[],
|
|
502
518
|
): TransitionResult<Draft08MessageType> {
|
|
503
|
-
const err = this.requireReady(message.type)
|
|
504
|
-
if (err) return { ok: false, violation: err }
|
|
505
|
-
const ok = message as import(
|
|
506
|
-
const nsKey = ok.track_namespace.join(
|
|
507
|
-
const existing = this._announces.get(nsKey)
|
|
519
|
+
const err = this.requireReady(message.type)
|
|
520
|
+
if (err) return { ok: false, violation: err }
|
|
521
|
+
const ok = message as import('./types.js').Draft08AnnounceOk
|
|
522
|
+
const nsKey = ok.track_namespace.join('/')
|
|
523
|
+
const existing = this._announces.get(nsKey)
|
|
508
524
|
if (!existing)
|
|
509
525
|
return {
|
|
510
526
|
ok: false,
|
|
511
527
|
violation: violation(
|
|
512
|
-
|
|
528
|
+
'UNKNOWN_REQUEST_ID',
|
|
513
529
|
`No announce for namespace ${nsKey}`,
|
|
514
530
|
this._phase,
|
|
515
531
|
message.type,
|
|
516
532
|
),
|
|
517
|
-
}
|
|
518
|
-
if (existing.phase !==
|
|
533
|
+
}
|
|
534
|
+
if (existing.phase !== 'pending')
|
|
519
535
|
return {
|
|
520
536
|
ok: false,
|
|
521
537
|
violation: violation(
|
|
522
|
-
|
|
538
|
+
'STATE_VIOLATION',
|
|
523
539
|
`Announce ${nsKey} is ${existing.phase}, not pending`,
|
|
524
540
|
this._phase,
|
|
525
541
|
message.type,
|
|
526
542
|
),
|
|
527
|
-
}
|
|
528
|
-
this._announces.set(nsKey, { ...existing, phase:
|
|
529
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
543
|
+
}
|
|
544
|
+
this._announces.set(nsKey, { ...existing, phase: 'active' })
|
|
545
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
530
546
|
}
|
|
531
547
|
|
|
532
548
|
private handleAnnounceError(
|
|
533
549
|
message: Draft08Message,
|
|
534
550
|
sideEffects: SideEffect[],
|
|
535
551
|
): TransitionResult<Draft08MessageType> {
|
|
536
|
-
const err = this.requireReady(message.type)
|
|
537
|
-
if (err) return { ok: false, violation: err }
|
|
538
|
-
const annErr = message as import(
|
|
539
|
-
const nsKey = annErr.track_namespace.join(
|
|
540
|
-
const existing = this._announces.get(nsKey)
|
|
552
|
+
const err = this.requireReady(message.type)
|
|
553
|
+
if (err) return { ok: false, violation: err }
|
|
554
|
+
const annErr = message as import('./types.js').Draft08AnnounceError
|
|
555
|
+
const nsKey = annErr.track_namespace.join('/')
|
|
556
|
+
const existing = this._announces.get(nsKey)
|
|
541
557
|
if (!existing)
|
|
542
558
|
return {
|
|
543
559
|
ok: false,
|
|
544
560
|
violation: violation(
|
|
545
|
-
|
|
561
|
+
'UNKNOWN_REQUEST_ID',
|
|
546
562
|
`No announce for namespace ${nsKey}`,
|
|
547
563
|
this._phase,
|
|
548
564
|
message.type,
|
|
549
565
|
),
|
|
550
|
-
}
|
|
551
|
-
if (existing.phase !==
|
|
566
|
+
}
|
|
567
|
+
if (existing.phase !== 'pending')
|
|
552
568
|
return {
|
|
553
569
|
ok: false,
|
|
554
570
|
violation: violation(
|
|
555
|
-
|
|
571
|
+
'STATE_VIOLATION',
|
|
556
572
|
`Announce ${nsKey} is ${existing.phase}, not pending`,
|
|
557
573
|
this._phase,
|
|
558
574
|
message.type,
|
|
559
575
|
),
|
|
560
|
-
}
|
|
561
|
-
this._announces.set(nsKey, { ...existing, phase:
|
|
562
|
-
sideEffects.push({
|
|
563
|
-
|
|
576
|
+
}
|
|
577
|
+
this._announces.set(nsKey, { ...existing, phase: 'error' })
|
|
578
|
+
sideEffects.push({
|
|
579
|
+
type: 'announce-ended',
|
|
580
|
+
namespace: annErr.track_namespace,
|
|
581
|
+
})
|
|
582
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
564
583
|
}
|
|
565
584
|
|
|
566
585
|
private handleUnannounce(
|
|
567
586
|
message: Draft08Message,
|
|
568
587
|
sideEffects: SideEffect[],
|
|
569
588
|
): TransitionResult<Draft08MessageType> {
|
|
570
|
-
const err = this.requireReady(message.type)
|
|
571
|
-
if (err) return { ok: false, violation: err }
|
|
572
|
-
const unann = message as import(
|
|
573
|
-
const nsKey = unann.track_namespace.join(
|
|
574
|
-
const existing = this._announces.get(nsKey)
|
|
589
|
+
const err = this.requireReady(message.type)
|
|
590
|
+
if (err) return { ok: false, violation: err }
|
|
591
|
+
const unann = message as import('./types.js').Draft08Unannounce
|
|
592
|
+
const nsKey = unann.track_namespace.join('/')
|
|
593
|
+
const existing = this._announces.get(nsKey)
|
|
575
594
|
if (existing) {
|
|
576
|
-
this._announces.delete(nsKey)
|
|
577
|
-
sideEffects.push({
|
|
595
|
+
this._announces.delete(nsKey)
|
|
596
|
+
sideEffects.push({
|
|
597
|
+
type: 'announce-ended',
|
|
598
|
+
namespace: unann.track_namespace,
|
|
599
|
+
})
|
|
578
600
|
}
|
|
579
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
601
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
580
602
|
}
|
|
581
603
|
|
|
582
604
|
private handleAnnounceCancel(
|
|
583
605
|
message: Draft08Message,
|
|
584
606
|
sideEffects: SideEffect[],
|
|
585
607
|
): TransitionResult<Draft08MessageType> {
|
|
586
|
-
const err = this.requireReady(message.type)
|
|
587
|
-
if (err) return { ok: false, violation: err }
|
|
588
|
-
const cancel = message as import(
|
|
589
|
-
const nsKey = cancel.track_namespace.join(
|
|
590
|
-
const existing = this._announces.get(nsKey)
|
|
608
|
+
const err = this.requireReady(message.type)
|
|
609
|
+
if (err) return { ok: false, violation: err }
|
|
610
|
+
const cancel = message as import('./types.js').Draft08AnnounceCancel
|
|
611
|
+
const nsKey = cancel.track_namespace.join('/')
|
|
612
|
+
const existing = this._announces.get(nsKey)
|
|
591
613
|
if (existing) {
|
|
592
|
-
this._announces.delete(nsKey)
|
|
593
|
-
sideEffects.push({
|
|
614
|
+
this._announces.delete(nsKey)
|
|
615
|
+
sideEffects.push({
|
|
616
|
+
type: 'announce-ended',
|
|
617
|
+
namespace: cancel.track_namespace,
|
|
618
|
+
})
|
|
594
619
|
}
|
|
595
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
620
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
596
621
|
}
|
|
597
622
|
|
|
598
623
|
// ─── Fetch lifecycle ───
|
|
@@ -601,118 +626,125 @@ export class Draft08SessionFSM {
|
|
|
601
626
|
message: Draft08Message,
|
|
602
627
|
sideEffects: SideEffect[],
|
|
603
628
|
): TransitionResult<Draft08MessageType> {
|
|
604
|
-
const err = this.requireReady(message.type)
|
|
605
|
-
if (err) return { ok: false, violation: err }
|
|
606
|
-
const fetch = message as import(
|
|
607
|
-
const dupErr = this.checkDuplicateSubscribeId(fetch.subscribe_id, message.type)
|
|
608
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
609
|
-
this._subscribeIds.add(fetch.subscribe_id)
|
|
610
|
-
this._fetches.set(fetch.subscribe_id, {
|
|
611
|
-
|
|
629
|
+
const err = this.requireReady(message.type)
|
|
630
|
+
if (err) return { ok: false, violation: err }
|
|
631
|
+
const fetch = message as import('./types.js').Draft08Fetch
|
|
632
|
+
const dupErr = this.checkDuplicateSubscribeId(fetch.subscribe_id, message.type)
|
|
633
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
634
|
+
this._subscribeIds.add(fetch.subscribe_id)
|
|
635
|
+
this._fetches.set(fetch.subscribe_id, {
|
|
636
|
+
requestId: fetch.subscribe_id,
|
|
637
|
+
phase: 'pending',
|
|
638
|
+
})
|
|
639
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
612
640
|
}
|
|
613
641
|
|
|
614
642
|
private handleFetchOk(
|
|
615
643
|
message: Draft08Message,
|
|
616
644
|
sideEffects: SideEffect[],
|
|
617
645
|
): TransitionResult<Draft08MessageType> {
|
|
618
|
-
const err = this.requireReady(message.type)
|
|
619
|
-
if (err) return { ok: false, violation: err }
|
|
620
|
-
const ok = message as import(
|
|
621
|
-
const idErr = this.checkKnownSubscribeId(ok.subscribe_id, message.type)
|
|
622
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
623
|
-
const existing = this._fetches.get(ok.subscribe_id)
|
|
646
|
+
const err = this.requireReady(message.type)
|
|
647
|
+
if (err) return { ok: false, violation: err }
|
|
648
|
+
const ok = message as import('./types.js').Draft08FetchOk
|
|
649
|
+
const idErr = this.checkKnownSubscribeId(ok.subscribe_id, message.type)
|
|
650
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
651
|
+
const existing = this._fetches.get(ok.subscribe_id)
|
|
624
652
|
if (!existing)
|
|
625
653
|
return {
|
|
626
654
|
ok: false,
|
|
627
655
|
violation: violation(
|
|
628
|
-
|
|
656
|
+
'UNKNOWN_REQUEST_ID',
|
|
629
657
|
`No fetch with subscribe ID ${ok.subscribe_id}`,
|
|
630
658
|
this._phase,
|
|
631
659
|
message.type,
|
|
632
660
|
),
|
|
633
|
-
}
|
|
634
|
-
if (existing.phase !==
|
|
661
|
+
}
|
|
662
|
+
if (existing.phase !== 'pending')
|
|
635
663
|
return {
|
|
636
664
|
ok: false,
|
|
637
665
|
violation: violation(
|
|
638
|
-
|
|
666
|
+
'STATE_VIOLATION',
|
|
639
667
|
`Fetch ${ok.subscribe_id} is ${existing.phase}, not pending`,
|
|
640
668
|
this._phase,
|
|
641
669
|
message.type,
|
|
642
670
|
),
|
|
643
|
-
}
|
|
644
|
-
this._fetches.set(ok.subscribe_id, { ...existing, phase:
|
|
645
|
-
sideEffects.push({ type:
|
|
646
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
671
|
+
}
|
|
672
|
+
this._fetches.set(ok.subscribe_id, { ...existing, phase: 'active' })
|
|
673
|
+
sideEffects.push({ type: 'fetch-activated', requestId: ok.subscribe_id })
|
|
674
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
647
675
|
}
|
|
648
676
|
|
|
649
677
|
private handleFetchError(
|
|
650
678
|
message: Draft08Message,
|
|
651
679
|
sideEffects: SideEffect[],
|
|
652
680
|
): TransitionResult<Draft08MessageType> {
|
|
653
|
-
const err = this.requireReady(message.type)
|
|
654
|
-
if (err) return { ok: false, violation: err }
|
|
655
|
-
const fetchErr = message as import(
|
|
656
|
-
const idErr = this.checkKnownSubscribeId(fetchErr.subscribe_id, message.type)
|
|
657
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
658
|
-
const existing = this._fetches.get(fetchErr.subscribe_id)
|
|
681
|
+
const err = this.requireReady(message.type)
|
|
682
|
+
if (err) return { ok: false, violation: err }
|
|
683
|
+
const fetchErr = message as import('./types.js').Draft08FetchError
|
|
684
|
+
const idErr = this.checkKnownSubscribeId(fetchErr.subscribe_id, message.type)
|
|
685
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
686
|
+
const existing = this._fetches.get(fetchErr.subscribe_id)
|
|
659
687
|
if (!existing)
|
|
660
688
|
return {
|
|
661
689
|
ok: false,
|
|
662
690
|
violation: violation(
|
|
663
|
-
|
|
691
|
+
'UNKNOWN_REQUEST_ID',
|
|
664
692
|
`No fetch with subscribe ID ${fetchErr.subscribe_id}`,
|
|
665
693
|
this._phase,
|
|
666
694
|
message.type,
|
|
667
695
|
),
|
|
668
|
-
}
|
|
669
|
-
this._fetches.set(fetchErr.subscribe_id, { ...existing, phase:
|
|
696
|
+
}
|
|
697
|
+
this._fetches.set(fetchErr.subscribe_id, { ...existing, phase: 'error' })
|
|
670
698
|
sideEffects.push({
|
|
671
|
-
type:
|
|
699
|
+
type: 'fetch-ended',
|
|
672
700
|
requestId: fetchErr.subscribe_id,
|
|
673
701
|
reason: fetchErr.reason_phrase,
|
|
674
|
-
})
|
|
675
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
702
|
+
})
|
|
703
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
676
704
|
}
|
|
677
705
|
|
|
678
706
|
private handleFetchCancel(
|
|
679
707
|
message: Draft08Message,
|
|
680
708
|
sideEffects: SideEffect[],
|
|
681
709
|
): TransitionResult<Draft08MessageType> {
|
|
682
|
-
const err = this.requireReady(message.type)
|
|
683
|
-
if (err) return { ok: false, violation: err }
|
|
684
|
-
const cancel = message as import(
|
|
685
|
-
const idErr = this.checkKnownSubscribeId(cancel.subscribe_id, message.type)
|
|
686
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
687
|
-
const existing = this._fetches.get(cancel.subscribe_id)
|
|
710
|
+
const err = this.requireReady(message.type)
|
|
711
|
+
if (err) return { ok: false, violation: err }
|
|
712
|
+
const cancel = message as import('./types.js').Draft08FetchCancel
|
|
713
|
+
const idErr = this.checkKnownSubscribeId(cancel.subscribe_id, message.type)
|
|
714
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
715
|
+
const existing = this._fetches.get(cancel.subscribe_id)
|
|
688
716
|
if (!existing)
|
|
689
717
|
return {
|
|
690
718
|
ok: false,
|
|
691
719
|
violation: violation(
|
|
692
|
-
|
|
720
|
+
'UNKNOWN_REQUEST_ID',
|
|
693
721
|
`No fetch with subscribe ID ${cancel.subscribe_id}`,
|
|
694
722
|
this._phase,
|
|
695
723
|
message.type,
|
|
696
724
|
),
|
|
697
|
-
}
|
|
698
|
-
this._fetches.set(cancel.subscribe_id, { ...existing, phase:
|
|
699
|
-
sideEffects.push({
|
|
700
|
-
|
|
725
|
+
}
|
|
726
|
+
this._fetches.set(cancel.subscribe_id, { ...existing, phase: 'cancelled' })
|
|
727
|
+
sideEffects.push({
|
|
728
|
+
type: 'fetch-ended',
|
|
729
|
+
requestId: cancel.subscribe_id,
|
|
730
|
+
reason: 'cancelled',
|
|
731
|
+
})
|
|
732
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
701
733
|
}
|
|
702
734
|
|
|
703
735
|
// ─── Generic ready-phase handler ───
|
|
704
736
|
|
|
705
737
|
private handleReadyPhaseMessage(message: Draft08Message): TransitionResult<Draft08MessageType> {
|
|
706
|
-
const err = this.requireReady(message.type)
|
|
707
|
-
if (err) return { ok: false, violation: err }
|
|
708
|
-
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
738
|
+
const err = this.requireReady(message.type)
|
|
739
|
+
if (err) return { ok: false, violation: err }
|
|
740
|
+
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
709
741
|
}
|
|
710
742
|
|
|
711
743
|
reset(): void {
|
|
712
|
-
this._phase =
|
|
713
|
-
this._subscriptions.clear()
|
|
714
|
-
this._announces.clear()
|
|
715
|
-
this._fetches.clear()
|
|
716
|
-
this._subscribeIds.clear()
|
|
744
|
+
this._phase = 'idle'
|
|
745
|
+
this._subscriptions.clear()
|
|
746
|
+
this._announces.clear()
|
|
747
|
+
this._fetches.clear()
|
|
748
|
+
this._subscribeIds.clear()
|
|
717
749
|
}
|
|
718
750
|
}
|