@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
|
@@ -8,88 +8,88 @@ import type {
|
|
|
8
8
|
SubscriptionState,
|
|
9
9
|
TransitionResult,
|
|
10
10
|
ValidationResult,
|
|
11
|
-
} from
|
|
11
|
+
} from '../../core/session-types.js'
|
|
12
12
|
import {
|
|
13
13
|
CLIENT_ONLY_MESSAGES,
|
|
14
14
|
getLegalIncoming,
|
|
15
15
|
getLegalOutgoing,
|
|
16
16
|
SERVER_ONLY_MESSAGES,
|
|
17
|
-
} from
|
|
18
|
-
import type { Draft14Message, Draft14MessageType } from
|
|
17
|
+
} from './rules.js'
|
|
18
|
+
import type { Draft14Message, Draft14MessageType } from './types.js'
|
|
19
19
|
|
|
20
20
|
function violation(
|
|
21
|
-
code: ProtocolViolation<Draft14MessageType>[
|
|
21
|
+
code: ProtocolViolation<Draft14MessageType>['code'],
|
|
22
22
|
message: string,
|
|
23
23
|
currentPhase: SessionPhase,
|
|
24
24
|
offendingMessage: Draft14MessageType,
|
|
25
25
|
): ProtocolViolation<Draft14MessageType> {
|
|
26
|
-
return { code, message, currentPhase, offendingMessage }
|
|
26
|
+
return { code, message, currentPhase, offendingMessage }
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export class Draft14SessionFSM {
|
|
30
|
-
private _phase: SessionPhase =
|
|
31
|
-
private _role:
|
|
32
|
-
private _subscriptions = new Map<bigint, SubscriptionState>()
|
|
33
|
-
private _publishes = new Map<bigint, PublishState>()
|
|
34
|
-
private _fetches = new Map<bigint, FetchState>()
|
|
35
|
-
private _requestIds = new Set<bigint>()
|
|
30
|
+
private _phase: SessionPhase = 'idle'
|
|
31
|
+
private _role: 'client' | 'server'
|
|
32
|
+
private _subscriptions = new Map<bigint, SubscriptionState>()
|
|
33
|
+
private _publishes = new Map<bigint, PublishState>()
|
|
34
|
+
private _fetches = new Map<bigint, FetchState>()
|
|
35
|
+
private _requestIds = new Set<bigint>()
|
|
36
36
|
|
|
37
|
-
constructor(role:
|
|
38
|
-
this._role = role
|
|
37
|
+
constructor(role: 'client' | 'server') {
|
|
38
|
+
this._role = role
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
get phase(): SessionPhase {
|
|
42
|
-
return this._phase
|
|
42
|
+
return this._phase
|
|
43
43
|
}
|
|
44
|
-
get role():
|
|
45
|
-
return this._role
|
|
44
|
+
get role(): 'client' | 'server' {
|
|
45
|
+
return this._role
|
|
46
46
|
}
|
|
47
47
|
get subscriptions(): ReadonlyMap<bigint, SubscriptionState> {
|
|
48
|
-
return this._subscriptions
|
|
48
|
+
return this._subscriptions
|
|
49
49
|
}
|
|
50
50
|
get announces(): ReadonlyMap<string, AnnounceState> {
|
|
51
|
-
return new Map()
|
|
51
|
+
return new Map()
|
|
52
52
|
}
|
|
53
53
|
get publishes(): ReadonlyMap<bigint, PublishState> {
|
|
54
|
-
return this._publishes
|
|
54
|
+
return this._publishes
|
|
55
55
|
}
|
|
56
56
|
get fetches(): ReadonlyMap<bigint, FetchState> {
|
|
57
|
-
return this._fetches
|
|
57
|
+
return this._fetches
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
get legalOutgoing(): ReadonlySet<Draft14MessageType> {
|
|
61
|
-
return getLegalOutgoing(this._phase, this._role)
|
|
61
|
+
return getLegalOutgoing(this._phase, this._role)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
get legalIncoming(): ReadonlySet<Draft14MessageType> {
|
|
65
|
-
return getLegalIncoming(this._phase, this._role)
|
|
65
|
+
return getLegalIncoming(this._phase, this._role)
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// Validate role constraints
|
|
69
69
|
private checkRole(
|
|
70
70
|
message: Draft14Message,
|
|
71
|
-
direction:
|
|
71
|
+
direction: 'inbound' | 'outbound',
|
|
72
72
|
): ProtocolViolation<Draft14MessageType> | null {
|
|
73
73
|
const senderRole =
|
|
74
|
-
direction ===
|
|
74
|
+
direction === 'outbound' ? this._role : this._role === 'client' ? 'server' : 'client'
|
|
75
75
|
|
|
76
|
-
if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !==
|
|
76
|
+
if (CLIENT_ONLY_MESSAGES.has(message.type) && senderRole !== 'client') {
|
|
77
77
|
return violation(
|
|
78
|
-
|
|
78
|
+
'ROLE_VIOLATION',
|
|
79
79
|
`${message.type} can only be sent by client`,
|
|
80
80
|
this._phase,
|
|
81
81
|
message.type,
|
|
82
|
-
)
|
|
82
|
+
)
|
|
83
83
|
}
|
|
84
|
-
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !==
|
|
84
|
+
if (SERVER_ONLY_MESSAGES.has(message.type) && senderRole !== 'server') {
|
|
85
85
|
return violation(
|
|
86
|
-
|
|
86
|
+
'ROLE_VIOLATION',
|
|
87
87
|
`${message.type} can only be sent by server`,
|
|
88
88
|
this._phase,
|
|
89
89
|
message.type,
|
|
90
|
-
)
|
|
90
|
+
)
|
|
91
91
|
}
|
|
92
|
-
return null
|
|
92
|
+
return null
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
private checkDuplicateRequestId(
|
|
@@ -98,13 +98,13 @@ export class Draft14SessionFSM {
|
|
|
98
98
|
): ProtocolViolation<Draft14MessageType> | null {
|
|
99
99
|
if (this._requestIds.has(requestId)) {
|
|
100
100
|
return violation(
|
|
101
|
-
|
|
101
|
+
'DUPLICATE_REQUEST_ID',
|
|
102
102
|
`Request ID ${requestId} already in use`,
|
|
103
103
|
this._phase,
|
|
104
104
|
msgType,
|
|
105
|
-
)
|
|
105
|
+
)
|
|
106
106
|
}
|
|
107
|
-
return null
|
|
107
|
+
return null
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
private checkKnownRequestId(
|
|
@@ -113,695 +113,709 @@ export class Draft14SessionFSM {
|
|
|
113
113
|
): ProtocolViolation<Draft14MessageType> | null {
|
|
114
114
|
if (!this._requestIds.has(requestId)) {
|
|
115
115
|
return violation(
|
|
116
|
-
|
|
116
|
+
'UNKNOWN_REQUEST_ID',
|
|
117
117
|
`No request with ID ${requestId}`,
|
|
118
118
|
this._phase,
|
|
119
119
|
msgType,
|
|
120
|
-
)
|
|
120
|
+
)
|
|
121
121
|
}
|
|
122
|
-
return null
|
|
122
|
+
return null
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
validateOutgoing(message: Draft14Message): ValidationResult<Draft14MessageType> {
|
|
126
|
-
const roleViolation = this.checkRole(message,
|
|
127
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
126
|
+
const roleViolation = this.checkRole(message, 'outbound')
|
|
127
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
128
128
|
|
|
129
129
|
if (!this.legalOutgoing.has(message.type)) {
|
|
130
130
|
return {
|
|
131
131
|
ok: false,
|
|
132
132
|
violation: violation(
|
|
133
|
-
this._phase ===
|
|
134
|
-
?
|
|
135
|
-
:
|
|
133
|
+
this._phase === 'idle' || this._phase === 'setup'
|
|
134
|
+
? 'MESSAGE_BEFORE_SETUP'
|
|
135
|
+
: 'UNEXPECTED_MESSAGE',
|
|
136
136
|
`Cannot send ${message.type} in phase ${this._phase}`,
|
|
137
137
|
this._phase,
|
|
138
138
|
message.type,
|
|
139
139
|
),
|
|
140
|
-
}
|
|
140
|
+
}
|
|
141
141
|
}
|
|
142
|
-
return { ok: true }
|
|
142
|
+
return { ok: true }
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
receive(message: Draft14Message): TransitionResult<Draft14MessageType> {
|
|
146
|
-
const roleViolation = this.checkRole(message,
|
|
147
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
146
|
+
const roleViolation = this.checkRole(message, 'inbound')
|
|
147
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
148
148
|
|
|
149
|
-
return this.applyTransition(message,
|
|
149
|
+
return this.applyTransition(message, 'inbound')
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
send(message: Draft14Message): TransitionResult<Draft14MessageType> {
|
|
153
|
-
const roleViolation = this.checkRole(message,
|
|
154
|
-
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
153
|
+
const roleViolation = this.checkRole(message, 'outbound')
|
|
154
|
+
if (roleViolation) return { ok: false, violation: roleViolation }
|
|
155
155
|
|
|
156
|
-
return this.applyTransition(message,
|
|
156
|
+
return this.applyTransition(message, 'outbound')
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
private applyTransition(
|
|
160
160
|
message: Draft14Message,
|
|
161
|
-
direction:
|
|
161
|
+
direction: 'inbound' | 'outbound',
|
|
162
162
|
): TransitionResult<Draft14MessageType> {
|
|
163
|
-
const sideEffects: SideEffect[] = []
|
|
163
|
+
const sideEffects: SideEffect[] = []
|
|
164
164
|
|
|
165
165
|
switch (message.type) {
|
|
166
|
-
case
|
|
167
|
-
return this.handleClientSetup(message, direction)
|
|
168
|
-
case
|
|
169
|
-
return this.handleServerSetup(message, direction)
|
|
170
|
-
case
|
|
171
|
-
return this.handleGoAway(message, direction, sideEffects)
|
|
166
|
+
case 'client_setup':
|
|
167
|
+
return this.handleClientSetup(message, direction)
|
|
168
|
+
case 'server_setup':
|
|
169
|
+
return this.handleServerSetup(message, direction)
|
|
170
|
+
case 'goaway':
|
|
171
|
+
return this.handleGoAway(message, direction, sideEffects)
|
|
172
172
|
|
|
173
173
|
// Subscribe lifecycle
|
|
174
|
-
case
|
|
175
|
-
return this.handleSubscribe(message, direction, sideEffects)
|
|
176
|
-
case
|
|
177
|
-
return this.handleSubscribeOk(message, direction, sideEffects)
|
|
178
|
-
case
|
|
179
|
-
return this.handleSubscribeError(message, direction, sideEffects)
|
|
180
|
-
case
|
|
181
|
-
return this.handleSubscribeUpdate(message, direction, sideEffects)
|
|
182
|
-
case
|
|
183
|
-
return this.handleUnsubscribe(message, direction, sideEffects)
|
|
174
|
+
case 'subscribe':
|
|
175
|
+
return this.handleSubscribe(message, direction, sideEffects)
|
|
176
|
+
case 'subscribe_ok':
|
|
177
|
+
return this.handleSubscribeOk(message, direction, sideEffects)
|
|
178
|
+
case 'subscribe_error':
|
|
179
|
+
return this.handleSubscribeError(message, direction, sideEffects)
|
|
180
|
+
case 'subscribe_update':
|
|
181
|
+
return this.handleSubscribeUpdate(message, direction, sideEffects)
|
|
182
|
+
case 'unsubscribe':
|
|
183
|
+
return this.handleUnsubscribe(message, direction, sideEffects)
|
|
184
184
|
|
|
185
185
|
// Publish lifecycle
|
|
186
|
-
case
|
|
187
|
-
return this.handlePublish(message, direction, sideEffects)
|
|
188
|
-
case
|
|
189
|
-
return this.handlePublishOk(message, direction, sideEffects)
|
|
190
|
-
case
|
|
191
|
-
return this.handlePublishError(message, direction, sideEffects)
|
|
192
|
-
case
|
|
193
|
-
return this.handlePublishDone(message, direction, sideEffects)
|
|
186
|
+
case 'publish':
|
|
187
|
+
return this.handlePublish(message, direction, sideEffects)
|
|
188
|
+
case 'publish_ok':
|
|
189
|
+
return this.handlePublishOk(message, direction, sideEffects)
|
|
190
|
+
case 'publish_error':
|
|
191
|
+
return this.handlePublishError(message, direction, sideEffects)
|
|
192
|
+
case 'publish_done':
|
|
193
|
+
return this.handlePublishDone(message, direction, sideEffects)
|
|
194
194
|
|
|
195
195
|
// Fetch lifecycle
|
|
196
|
-
case
|
|
197
|
-
return this.handleFetch(message, direction, sideEffects)
|
|
198
|
-
case
|
|
199
|
-
return this.handleFetchOk(message, direction, sideEffects)
|
|
200
|
-
case
|
|
201
|
-
return this.handleFetchError(message, direction, sideEffects)
|
|
202
|
-
case
|
|
203
|
-
return this.handleFetchCancel(message, direction, sideEffects)
|
|
196
|
+
case 'fetch':
|
|
197
|
+
return this.handleFetch(message, direction, sideEffects)
|
|
198
|
+
case 'fetch_ok':
|
|
199
|
+
return this.handleFetchOk(message, direction, sideEffects)
|
|
200
|
+
case 'fetch_error':
|
|
201
|
+
return this.handleFetchError(message, direction, sideEffects)
|
|
202
|
+
case 'fetch_cancel':
|
|
203
|
+
return this.handleFetchCancel(message, direction, sideEffects)
|
|
204
204
|
|
|
205
205
|
// Publish namespace lifecycle
|
|
206
|
-
case
|
|
207
|
-
return this.handlePublishNamespace(message, sideEffects)
|
|
208
|
-
case
|
|
209
|
-
return this.handlePublishNamespaceOk(message, sideEffects)
|
|
210
|
-
case
|
|
211
|
-
return this.handlePublishNamespaceError(message, sideEffects)
|
|
212
|
-
case
|
|
213
|
-
return this.handlePublishNamespaceDone(message, sideEffects)
|
|
214
|
-
case
|
|
215
|
-
return this.handlePublishNamespaceCancel(message, sideEffects)
|
|
206
|
+
case 'publish_namespace':
|
|
207
|
+
return this.handlePublishNamespace(message, sideEffects)
|
|
208
|
+
case 'publish_namespace_ok':
|
|
209
|
+
return this.handlePublishNamespaceOk(message, sideEffects)
|
|
210
|
+
case 'publish_namespace_error':
|
|
211
|
+
return this.handlePublishNamespaceError(message, sideEffects)
|
|
212
|
+
case 'publish_namespace_done':
|
|
213
|
+
return this.handlePublishNamespaceDone(message, sideEffects)
|
|
214
|
+
case 'publish_namespace_cancel':
|
|
215
|
+
return this.handlePublishNamespaceCancel(message, sideEffects)
|
|
216
216
|
|
|
217
217
|
// Subscribe namespace lifecycle
|
|
218
|
-
case
|
|
219
|
-
return this.handleSubscribeNamespace(message, sideEffects)
|
|
220
|
-
case
|
|
221
|
-
return this.handleSubscribeNamespaceOk(message, sideEffects)
|
|
222
|
-
case
|
|
223
|
-
return this.handleSubscribeNamespaceError(message, sideEffects)
|
|
224
|
-
case
|
|
225
|
-
return this.handleUnsubscribeNamespace(message, sideEffects)
|
|
218
|
+
case 'subscribe_namespace':
|
|
219
|
+
return this.handleSubscribeNamespace(message, sideEffects)
|
|
220
|
+
case 'subscribe_namespace_ok':
|
|
221
|
+
return this.handleSubscribeNamespaceOk(message, sideEffects)
|
|
222
|
+
case 'subscribe_namespace_error':
|
|
223
|
+
return this.handleSubscribeNamespaceError(message, sideEffects)
|
|
224
|
+
case 'unsubscribe_namespace':
|
|
225
|
+
return this.handleUnsubscribeNamespace(message, sideEffects)
|
|
226
226
|
|
|
227
227
|
// Track status lifecycle
|
|
228
|
-
case
|
|
229
|
-
return this.handleTrackStatus(message, sideEffects)
|
|
230
|
-
case
|
|
231
|
-
return this.handleTrackStatusOk(message, sideEffects)
|
|
232
|
-
case
|
|
233
|
-
return this.handleTrackStatusError(message, sideEffects)
|
|
228
|
+
case 'track_status':
|
|
229
|
+
return this.handleTrackStatus(message, sideEffects)
|
|
230
|
+
case 'track_status_ok':
|
|
231
|
+
return this.handleTrackStatusOk(message, sideEffects)
|
|
232
|
+
case 'track_status_error':
|
|
233
|
+
return this.handleTrackStatusError(message, sideEffects)
|
|
234
234
|
|
|
235
235
|
// Other ready-phase messages
|
|
236
236
|
default:
|
|
237
|
-
return this.handleReadyPhaseMessage(message)
|
|
237
|
+
return this.handleReadyPhaseMessage(message)
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
private handleClientSetup(
|
|
242
242
|
_message: Draft14Message,
|
|
243
|
-
direction:
|
|
243
|
+
direction: 'inbound' | 'outbound',
|
|
244
244
|
): TransitionResult<Draft14MessageType> {
|
|
245
|
-
if (this._phase !==
|
|
245
|
+
if (this._phase !== 'idle') {
|
|
246
246
|
return {
|
|
247
247
|
ok: false,
|
|
248
248
|
violation: violation(
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
'SETUP_VIOLATION',
|
|
250
|
+
'CLIENT_SETUP already sent/received',
|
|
251
251
|
this._phase,
|
|
252
|
-
|
|
252
|
+
'client_setup',
|
|
253
253
|
),
|
|
254
|
-
}
|
|
254
|
+
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
if (direction ===
|
|
257
|
+
if (direction === 'outbound' && this._role !== 'client') {
|
|
258
258
|
return {
|
|
259
259
|
ok: false,
|
|
260
260
|
violation: violation(
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
'ROLE_VIOLATION',
|
|
262
|
+
'Only client can send CLIENT_SETUP',
|
|
263
263
|
this._phase,
|
|
264
|
-
|
|
264
|
+
'client_setup',
|
|
265
265
|
),
|
|
266
|
-
}
|
|
266
|
+
}
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
this._phase =
|
|
270
|
-
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
269
|
+
this._phase = 'setup'
|
|
270
|
+
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
private handleServerSetup(
|
|
274
274
|
_message: Draft14Message,
|
|
275
|
-
direction:
|
|
275
|
+
direction: 'inbound' | 'outbound',
|
|
276
276
|
): TransitionResult<Draft14MessageType> {
|
|
277
|
-
if (this._phase !==
|
|
277
|
+
if (this._phase !== 'setup') {
|
|
278
278
|
return {
|
|
279
279
|
ok: false,
|
|
280
280
|
violation: violation(
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
'SETUP_VIOLATION',
|
|
282
|
+
'SERVER_SETUP before CLIENT_SETUP',
|
|
283
283
|
this._phase,
|
|
284
|
-
|
|
284
|
+
'server_setup',
|
|
285
285
|
),
|
|
286
|
-
}
|
|
286
|
+
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
-
if (direction ===
|
|
289
|
+
if (direction === 'outbound' && this._role !== 'server') {
|
|
290
290
|
return {
|
|
291
291
|
ok: false,
|
|
292
292
|
violation: violation(
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
'ROLE_VIOLATION',
|
|
294
|
+
'Only server can send SERVER_SETUP',
|
|
295
295
|
this._phase,
|
|
296
|
-
|
|
296
|
+
'server_setup',
|
|
297
297
|
),
|
|
298
|
-
}
|
|
298
|
+
}
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
this._phase =
|
|
302
|
-
return {
|
|
301
|
+
this._phase = 'ready'
|
|
302
|
+
return {
|
|
303
|
+
ok: true,
|
|
304
|
+
phase: this._phase,
|
|
305
|
+
sideEffects: [{ type: 'session-ready' }],
|
|
306
|
+
}
|
|
303
307
|
}
|
|
304
308
|
|
|
305
309
|
private handleGoAway(
|
|
306
310
|
message: Draft14Message,
|
|
307
|
-
_direction:
|
|
311
|
+
_direction: 'inbound' | 'outbound',
|
|
308
312
|
sideEffects: SideEffect[],
|
|
309
313
|
): TransitionResult<Draft14MessageType> {
|
|
310
|
-
if (this._phase !==
|
|
314
|
+
if (this._phase !== 'ready' && this._phase !== 'draining') {
|
|
311
315
|
return {
|
|
312
316
|
ok: false,
|
|
313
317
|
violation: violation(
|
|
314
|
-
|
|
318
|
+
'UNEXPECTED_MESSAGE',
|
|
315
319
|
`GOAWAY not valid in phase ${this._phase}`,
|
|
316
320
|
this._phase,
|
|
317
|
-
|
|
321
|
+
'goaway',
|
|
318
322
|
),
|
|
319
|
-
}
|
|
323
|
+
}
|
|
320
324
|
}
|
|
321
|
-
this._phase =
|
|
322
|
-
const goaway = message as import(
|
|
323
|
-
sideEffects.push({
|
|
324
|
-
|
|
325
|
+
this._phase = 'draining'
|
|
326
|
+
const goaway = message as import('./types.js').Draft14GoAway
|
|
327
|
+
sideEffects.push({
|
|
328
|
+
type: 'session-draining',
|
|
329
|
+
goAwayUri: goaway.new_session_uri,
|
|
330
|
+
})
|
|
331
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
325
332
|
}
|
|
326
333
|
|
|
327
334
|
private requireReady(msgType: Draft14MessageType): ProtocolViolation<Draft14MessageType> | null {
|
|
328
|
-
if (this._phase !==
|
|
335
|
+
if (this._phase !== 'ready' && this._phase !== 'draining') {
|
|
329
336
|
return violation(
|
|
330
|
-
this._phase ===
|
|
331
|
-
?
|
|
332
|
-
:
|
|
337
|
+
this._phase === 'idle' || this._phase === 'setup'
|
|
338
|
+
? 'MESSAGE_BEFORE_SETUP'
|
|
339
|
+
: 'UNEXPECTED_MESSAGE',
|
|
333
340
|
`${msgType} requires ready phase, current: ${this._phase}`,
|
|
334
341
|
this._phase,
|
|
335
342
|
msgType,
|
|
336
|
-
)
|
|
343
|
+
)
|
|
337
344
|
}
|
|
338
|
-
return null
|
|
345
|
+
return null
|
|
339
346
|
}
|
|
340
347
|
|
|
341
348
|
// ─── Subscribe lifecycle ──────────────────────────────────────────────────────
|
|
342
349
|
|
|
343
350
|
private handleSubscribe(
|
|
344
351
|
message: Draft14Message,
|
|
345
|
-
_direction:
|
|
352
|
+
_direction: 'inbound' | 'outbound',
|
|
346
353
|
sideEffects: SideEffect[],
|
|
347
354
|
): TransitionResult<Draft14MessageType> {
|
|
348
|
-
const err = this.requireReady(message.type)
|
|
349
|
-
if (err) return { ok: false, violation: err }
|
|
355
|
+
const err = this.requireReady(message.type)
|
|
356
|
+
if (err) return { ok: false, violation: err }
|
|
350
357
|
|
|
351
|
-
const sub = message as import(
|
|
352
|
-
const dupErr = this.checkDuplicateRequestId(sub.request_id, message.type)
|
|
353
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
358
|
+
const sub = message as import('./types.js').Draft14Subscribe
|
|
359
|
+
const dupErr = this.checkDuplicateRequestId(sub.request_id, message.type)
|
|
360
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
354
361
|
|
|
355
|
-
this._requestIds.add(sub.request_id)
|
|
362
|
+
this._requestIds.add(sub.request_id)
|
|
356
363
|
this._subscriptions.set(sub.request_id, {
|
|
357
364
|
subscribeId: sub.request_id,
|
|
358
|
-
phase:
|
|
365
|
+
phase: 'pending',
|
|
359
366
|
trackNamespace: sub.track_namespace,
|
|
360
367
|
trackName: sub.track_name,
|
|
361
|
-
})
|
|
368
|
+
})
|
|
362
369
|
|
|
363
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
370
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
364
371
|
}
|
|
365
372
|
|
|
366
373
|
private handleSubscribeOk(
|
|
367
374
|
message: Draft14Message,
|
|
368
|
-
_direction:
|
|
375
|
+
_direction: 'inbound' | 'outbound',
|
|
369
376
|
sideEffects: SideEffect[],
|
|
370
377
|
): TransitionResult<Draft14MessageType> {
|
|
371
|
-
const err = this.requireReady(message.type)
|
|
372
|
-
if (err) return { ok: false, violation: err }
|
|
378
|
+
const err = this.requireReady(message.type)
|
|
379
|
+
if (err) return { ok: false, violation: err }
|
|
373
380
|
|
|
374
|
-
const ok = message as import(
|
|
375
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
376
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
381
|
+
const ok = message as import('./types.js').Draft14SubscribeOk
|
|
382
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
383
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
377
384
|
|
|
378
|
-
const existing = this._subscriptions.get(ok.request_id)
|
|
385
|
+
const existing = this._subscriptions.get(ok.request_id)
|
|
379
386
|
if (!existing) {
|
|
380
387
|
return {
|
|
381
388
|
ok: false,
|
|
382
389
|
violation: violation(
|
|
383
|
-
|
|
390
|
+
'UNKNOWN_REQUEST_ID',
|
|
384
391
|
`No subscription with request ID ${ok.request_id}`,
|
|
385
392
|
this._phase,
|
|
386
393
|
message.type,
|
|
387
394
|
),
|
|
388
|
-
}
|
|
395
|
+
}
|
|
389
396
|
}
|
|
390
|
-
if (existing.phase !==
|
|
397
|
+
if (existing.phase !== 'pending') {
|
|
391
398
|
return {
|
|
392
399
|
ok: false,
|
|
393
400
|
violation: violation(
|
|
394
|
-
|
|
401
|
+
'STATE_VIOLATION',
|
|
395
402
|
`Subscription ${ok.request_id} is ${existing.phase}, not pending`,
|
|
396
403
|
this._phase,
|
|
397
404
|
message.type,
|
|
398
405
|
),
|
|
399
|
-
}
|
|
406
|
+
}
|
|
400
407
|
}
|
|
401
408
|
|
|
402
|
-
this._subscriptions.set(ok.request_id, { ...existing, phase:
|
|
403
|
-
sideEffects.push({
|
|
404
|
-
|
|
409
|
+
this._subscriptions.set(ok.request_id, { ...existing, phase: 'active' })
|
|
410
|
+
sideEffects.push({
|
|
411
|
+
type: 'subscription-activated',
|
|
412
|
+
subscribeId: ok.request_id,
|
|
413
|
+
})
|
|
414
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
405
415
|
}
|
|
406
416
|
|
|
407
417
|
private handleSubscribeError(
|
|
408
418
|
message: Draft14Message,
|
|
409
|
-
_direction:
|
|
419
|
+
_direction: 'inbound' | 'outbound',
|
|
410
420
|
sideEffects: SideEffect[],
|
|
411
421
|
): TransitionResult<Draft14MessageType> {
|
|
412
|
-
const err = this.requireReady(message.type)
|
|
413
|
-
if (err) return { ok: false, violation: err }
|
|
422
|
+
const err = this.requireReady(message.type)
|
|
423
|
+
if (err) return { ok: false, violation: err }
|
|
414
424
|
|
|
415
|
-
const subErr = message as import(
|
|
416
|
-
const idErr = this.checkKnownRequestId(subErr.request_id, message.type)
|
|
417
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
425
|
+
const subErr = message as import('./types.js').Draft14SubscribeError
|
|
426
|
+
const idErr = this.checkKnownRequestId(subErr.request_id, message.type)
|
|
427
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
418
428
|
|
|
419
|
-
const existing = this._subscriptions.get(subErr.request_id)
|
|
429
|
+
const existing = this._subscriptions.get(subErr.request_id)
|
|
420
430
|
if (!existing) {
|
|
421
431
|
return {
|
|
422
432
|
ok: false,
|
|
423
433
|
violation: violation(
|
|
424
|
-
|
|
434
|
+
'UNKNOWN_REQUEST_ID',
|
|
425
435
|
`No subscription with request ID ${subErr.request_id}`,
|
|
426
436
|
this._phase,
|
|
427
437
|
message.type,
|
|
428
438
|
),
|
|
429
|
-
}
|
|
439
|
+
}
|
|
430
440
|
}
|
|
431
|
-
if (existing.phase !==
|
|
441
|
+
if (existing.phase !== 'pending') {
|
|
432
442
|
return {
|
|
433
443
|
ok: false,
|
|
434
444
|
violation: violation(
|
|
435
|
-
|
|
445
|
+
'STATE_VIOLATION',
|
|
436
446
|
`Subscription ${subErr.request_id} is ${existing.phase}, not pending`,
|
|
437
447
|
this._phase,
|
|
438
448
|
message.type,
|
|
439
449
|
),
|
|
440
|
-
}
|
|
450
|
+
}
|
|
441
451
|
}
|
|
442
452
|
|
|
443
|
-
this._subscriptions.set(subErr.request_id, { ...existing, phase:
|
|
453
|
+
this._subscriptions.set(subErr.request_id, { ...existing, phase: 'error' })
|
|
444
454
|
sideEffects.push({
|
|
445
|
-
type:
|
|
455
|
+
type: 'subscription-ended',
|
|
446
456
|
subscribeId: subErr.request_id,
|
|
447
457
|
reason: subErr.reason_phrase,
|
|
448
|
-
})
|
|
449
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
458
|
+
})
|
|
459
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
450
460
|
}
|
|
451
461
|
|
|
452
462
|
private handleSubscribeUpdate(
|
|
453
463
|
message: Draft14Message,
|
|
454
|
-
_direction:
|
|
464
|
+
_direction: 'inbound' | 'outbound',
|
|
455
465
|
sideEffects: SideEffect[],
|
|
456
466
|
): TransitionResult<Draft14MessageType> {
|
|
457
|
-
const err = this.requireReady(message.type)
|
|
458
|
-
if (err) return { ok: false, violation: err }
|
|
467
|
+
const err = this.requireReady(message.type)
|
|
468
|
+
if (err) return { ok: false, violation: err }
|
|
459
469
|
|
|
460
|
-
const update = message as import(
|
|
461
|
-
const idErr = this.checkKnownRequestId(update.request_id, message.type)
|
|
462
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
470
|
+
const update = message as import('./types.js').Draft14SubscribeUpdate
|
|
471
|
+
const idErr = this.checkKnownRequestId(update.request_id, message.type)
|
|
472
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
463
473
|
|
|
464
|
-
const existing = this._subscriptions.get(update.request_id)
|
|
474
|
+
const existing = this._subscriptions.get(update.request_id)
|
|
465
475
|
if (!existing) {
|
|
466
476
|
return {
|
|
467
477
|
ok: false,
|
|
468
478
|
violation: violation(
|
|
469
|
-
|
|
479
|
+
'UNKNOWN_REQUEST_ID',
|
|
470
480
|
`No subscription with request ID ${update.request_id}`,
|
|
471
481
|
this._phase,
|
|
472
482
|
message.type,
|
|
473
483
|
),
|
|
474
|
-
}
|
|
484
|
+
}
|
|
475
485
|
}
|
|
476
|
-
if (existing.phase !==
|
|
486
|
+
if (existing.phase !== 'active') {
|
|
477
487
|
return {
|
|
478
488
|
ok: false,
|
|
479
489
|
violation: violation(
|
|
480
|
-
|
|
490
|
+
'STATE_VIOLATION',
|
|
481
491
|
`Subscription ${update.request_id} is ${existing.phase}, not active`,
|
|
482
492
|
this._phase,
|
|
483
493
|
message.type,
|
|
484
494
|
),
|
|
485
|
-
}
|
|
495
|
+
}
|
|
486
496
|
}
|
|
487
497
|
|
|
488
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
498
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
489
499
|
}
|
|
490
500
|
|
|
491
501
|
private handleUnsubscribe(
|
|
492
502
|
message: Draft14Message,
|
|
493
|
-
_direction:
|
|
503
|
+
_direction: 'inbound' | 'outbound',
|
|
494
504
|
sideEffects: SideEffect[],
|
|
495
505
|
): TransitionResult<Draft14MessageType> {
|
|
496
|
-
const err = this.requireReady(message.type)
|
|
497
|
-
if (err) return { ok: false, violation: err }
|
|
506
|
+
const err = this.requireReady(message.type)
|
|
507
|
+
if (err) return { ok: false, violation: err }
|
|
498
508
|
|
|
499
|
-
const unsub = message as import(
|
|
500
|
-
const idErr = this.checkKnownRequestId(unsub.request_id, message.type)
|
|
501
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
509
|
+
const unsub = message as import('./types.js').Draft14Unsubscribe
|
|
510
|
+
const idErr = this.checkKnownRequestId(unsub.request_id, message.type)
|
|
511
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
502
512
|
|
|
503
|
-
const existing = this._subscriptions.get(unsub.request_id)
|
|
513
|
+
const existing = this._subscriptions.get(unsub.request_id)
|
|
504
514
|
if (!existing) {
|
|
505
515
|
return {
|
|
506
516
|
ok: false,
|
|
507
517
|
violation: violation(
|
|
508
|
-
|
|
518
|
+
'UNKNOWN_REQUEST_ID',
|
|
509
519
|
`No subscription with request ID ${unsub.request_id}`,
|
|
510
520
|
this._phase,
|
|
511
521
|
message.type,
|
|
512
522
|
),
|
|
513
|
-
}
|
|
523
|
+
}
|
|
514
524
|
}
|
|
515
525
|
|
|
516
|
-
this._subscriptions.set(unsub.request_id, { ...existing, phase:
|
|
526
|
+
this._subscriptions.set(unsub.request_id, { ...existing, phase: 'done' })
|
|
517
527
|
sideEffects.push({
|
|
518
|
-
type:
|
|
528
|
+
type: 'subscription-ended',
|
|
519
529
|
subscribeId: unsub.request_id,
|
|
520
|
-
reason:
|
|
521
|
-
})
|
|
522
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
530
|
+
reason: 'unsubscribed',
|
|
531
|
+
})
|
|
532
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
523
533
|
}
|
|
524
534
|
|
|
525
535
|
// ─── Publish lifecycle ────────────────────────────────────────────────────────
|
|
526
536
|
|
|
527
537
|
private handlePublish(
|
|
528
538
|
message: Draft14Message,
|
|
529
|
-
_direction:
|
|
539
|
+
_direction: 'inbound' | 'outbound',
|
|
530
540
|
sideEffects: SideEffect[],
|
|
531
541
|
): TransitionResult<Draft14MessageType> {
|
|
532
|
-
const err = this.requireReady(message.type)
|
|
533
|
-
if (err) return { ok: false, violation: err }
|
|
542
|
+
const err = this.requireReady(message.type)
|
|
543
|
+
if (err) return { ok: false, violation: err }
|
|
534
544
|
|
|
535
|
-
const pub = message as import(
|
|
536
|
-
const dupErr = this.checkDuplicateRequestId(pub.request_id, message.type)
|
|
537
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
545
|
+
const pub = message as import('./types.js').Draft14Publish
|
|
546
|
+
const dupErr = this.checkDuplicateRequestId(pub.request_id, message.type)
|
|
547
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
538
548
|
|
|
539
|
-
this._requestIds.add(pub.request_id)
|
|
549
|
+
this._requestIds.add(pub.request_id)
|
|
540
550
|
this._publishes.set(pub.request_id, {
|
|
541
551
|
requestId: pub.request_id,
|
|
542
|
-
phase:
|
|
543
|
-
})
|
|
552
|
+
phase: 'pending',
|
|
553
|
+
})
|
|
544
554
|
|
|
545
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
555
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
546
556
|
}
|
|
547
557
|
|
|
548
558
|
private handlePublishOk(
|
|
549
559
|
message: Draft14Message,
|
|
550
|
-
_direction:
|
|
560
|
+
_direction: 'inbound' | 'outbound',
|
|
551
561
|
sideEffects: SideEffect[],
|
|
552
562
|
): TransitionResult<Draft14MessageType> {
|
|
553
|
-
const err = this.requireReady(message.type)
|
|
554
|
-
if (err) return { ok: false, violation: err }
|
|
563
|
+
const err = this.requireReady(message.type)
|
|
564
|
+
if (err) return { ok: false, violation: err }
|
|
555
565
|
|
|
556
|
-
const ok = message as import(
|
|
557
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
558
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
566
|
+
const ok = message as import('./types.js').Draft14PublishOk
|
|
567
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
568
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
559
569
|
|
|
560
|
-
const existing = this._publishes.get(ok.request_id)
|
|
570
|
+
const existing = this._publishes.get(ok.request_id)
|
|
561
571
|
if (!existing) {
|
|
562
572
|
return {
|
|
563
573
|
ok: false,
|
|
564
574
|
violation: violation(
|
|
565
|
-
|
|
575
|
+
'UNKNOWN_REQUEST_ID',
|
|
566
576
|
`No publish with request ID ${ok.request_id}`,
|
|
567
577
|
this._phase,
|
|
568
578
|
message.type,
|
|
569
579
|
),
|
|
570
|
-
}
|
|
580
|
+
}
|
|
571
581
|
}
|
|
572
|
-
if (existing.phase !==
|
|
582
|
+
if (existing.phase !== 'pending') {
|
|
573
583
|
return {
|
|
574
584
|
ok: false,
|
|
575
585
|
violation: violation(
|
|
576
|
-
|
|
586
|
+
'STATE_VIOLATION',
|
|
577
587
|
`Publish ${ok.request_id} is ${existing.phase}, not pending`,
|
|
578
588
|
this._phase,
|
|
579
589
|
message.type,
|
|
580
590
|
),
|
|
581
|
-
}
|
|
591
|
+
}
|
|
582
592
|
}
|
|
583
593
|
|
|
584
|
-
this._publishes.set(ok.request_id, { ...existing, phase:
|
|
585
|
-
sideEffects.push({ type:
|
|
586
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
594
|
+
this._publishes.set(ok.request_id, { ...existing, phase: 'active' })
|
|
595
|
+
sideEffects.push({ type: 'publish-activated', requestId: ok.request_id })
|
|
596
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
587
597
|
}
|
|
588
598
|
|
|
589
599
|
private handlePublishError(
|
|
590
600
|
message: Draft14Message,
|
|
591
|
-
_direction:
|
|
601
|
+
_direction: 'inbound' | 'outbound',
|
|
592
602
|
sideEffects: SideEffect[],
|
|
593
603
|
): TransitionResult<Draft14MessageType> {
|
|
594
|
-
const err = this.requireReady(message.type)
|
|
595
|
-
if (err) return { ok: false, violation: err }
|
|
604
|
+
const err = this.requireReady(message.type)
|
|
605
|
+
if (err) return { ok: false, violation: err }
|
|
596
606
|
|
|
597
|
-
const pubErr = message as import(
|
|
598
|
-
const idErr = this.checkKnownRequestId(pubErr.request_id, message.type)
|
|
599
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
607
|
+
const pubErr = message as import('./types.js').Draft14PublishError
|
|
608
|
+
const idErr = this.checkKnownRequestId(pubErr.request_id, message.type)
|
|
609
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
600
610
|
|
|
601
|
-
const existing = this._publishes.get(pubErr.request_id)
|
|
611
|
+
const existing = this._publishes.get(pubErr.request_id)
|
|
602
612
|
if (!existing) {
|
|
603
613
|
return {
|
|
604
614
|
ok: false,
|
|
605
615
|
violation: violation(
|
|
606
|
-
|
|
616
|
+
'UNKNOWN_REQUEST_ID',
|
|
607
617
|
`No publish with request ID ${pubErr.request_id}`,
|
|
608
618
|
this._phase,
|
|
609
619
|
message.type,
|
|
610
620
|
),
|
|
611
|
-
}
|
|
621
|
+
}
|
|
612
622
|
}
|
|
613
|
-
if (existing.phase !==
|
|
623
|
+
if (existing.phase !== 'pending') {
|
|
614
624
|
return {
|
|
615
625
|
ok: false,
|
|
616
626
|
violation: violation(
|
|
617
|
-
|
|
627
|
+
'STATE_VIOLATION',
|
|
618
628
|
`Publish ${pubErr.request_id} is ${existing.phase}, not pending`,
|
|
619
629
|
this._phase,
|
|
620
630
|
message.type,
|
|
621
631
|
),
|
|
622
|
-
}
|
|
632
|
+
}
|
|
623
633
|
}
|
|
624
634
|
|
|
625
|
-
this._publishes.set(pubErr.request_id, { ...existing, phase:
|
|
635
|
+
this._publishes.set(pubErr.request_id, { ...existing, phase: 'error' })
|
|
626
636
|
sideEffects.push({
|
|
627
|
-
type:
|
|
637
|
+
type: 'publish-ended',
|
|
628
638
|
requestId: pubErr.request_id,
|
|
629
639
|
reason: pubErr.reason_phrase,
|
|
630
|
-
})
|
|
631
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
640
|
+
})
|
|
641
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
632
642
|
}
|
|
633
643
|
|
|
634
644
|
private handlePublishDone(
|
|
635
645
|
message: Draft14Message,
|
|
636
|
-
_direction:
|
|
646
|
+
_direction: 'inbound' | 'outbound',
|
|
637
647
|
sideEffects: SideEffect[],
|
|
638
648
|
): TransitionResult<Draft14MessageType> {
|
|
639
|
-
const err = this.requireReady(message.type)
|
|
640
|
-
if (err) return { ok: false, violation: err }
|
|
649
|
+
const err = this.requireReady(message.type)
|
|
650
|
+
if (err) return { ok: false, violation: err }
|
|
641
651
|
|
|
642
|
-
const done = message as import(
|
|
643
|
-
const idErr = this.checkKnownRequestId(done.request_id, message.type)
|
|
644
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
652
|
+
const done = message as import('./types.js').Draft14PublishDone
|
|
653
|
+
const idErr = this.checkKnownRequestId(done.request_id, message.type)
|
|
654
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
645
655
|
|
|
646
|
-
const existing = this._publishes.get(done.request_id)
|
|
656
|
+
const existing = this._publishes.get(done.request_id)
|
|
647
657
|
if (!existing) {
|
|
648
658
|
return {
|
|
649
659
|
ok: false,
|
|
650
660
|
violation: violation(
|
|
651
|
-
|
|
661
|
+
'UNKNOWN_REQUEST_ID',
|
|
652
662
|
`No publish with request ID ${done.request_id}`,
|
|
653
663
|
this._phase,
|
|
654
664
|
message.type,
|
|
655
665
|
),
|
|
656
|
-
}
|
|
666
|
+
}
|
|
657
667
|
}
|
|
658
668
|
|
|
659
|
-
this._publishes.set(done.request_id, { ...existing, phase:
|
|
669
|
+
this._publishes.set(done.request_id, { ...existing, phase: 'done' })
|
|
660
670
|
sideEffects.push({
|
|
661
|
-
type:
|
|
671
|
+
type: 'publish-ended',
|
|
662
672
|
requestId: done.request_id,
|
|
663
673
|
reason: done.reason_phrase,
|
|
664
|
-
})
|
|
665
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
674
|
+
})
|
|
675
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
666
676
|
}
|
|
667
677
|
|
|
668
678
|
// ─── Fetch lifecycle ──────────────────────────────────────────────────────────
|
|
669
679
|
|
|
670
680
|
private handleFetch(
|
|
671
681
|
message: Draft14Message,
|
|
672
|
-
_direction:
|
|
682
|
+
_direction: 'inbound' | 'outbound',
|
|
673
683
|
sideEffects: SideEffect[],
|
|
674
684
|
): TransitionResult<Draft14MessageType> {
|
|
675
|
-
const err = this.requireReady(message.type)
|
|
676
|
-
if (err) return { ok: false, violation: err }
|
|
685
|
+
const err = this.requireReady(message.type)
|
|
686
|
+
if (err) return { ok: false, violation: err }
|
|
677
687
|
|
|
678
|
-
const fetch = message as import(
|
|
679
|
-
const dupErr = this.checkDuplicateRequestId(fetch.request_id, message.type)
|
|
680
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
688
|
+
const fetch = message as import('./types.js').Draft14Fetch
|
|
689
|
+
const dupErr = this.checkDuplicateRequestId(fetch.request_id, message.type)
|
|
690
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
681
691
|
|
|
682
|
-
this._requestIds.add(fetch.request_id)
|
|
692
|
+
this._requestIds.add(fetch.request_id)
|
|
683
693
|
this._fetches.set(fetch.request_id, {
|
|
684
694
|
requestId: fetch.request_id,
|
|
685
|
-
phase:
|
|
686
|
-
})
|
|
695
|
+
phase: 'pending',
|
|
696
|
+
})
|
|
687
697
|
|
|
688
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
698
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
689
699
|
}
|
|
690
700
|
|
|
691
701
|
private handleFetchOk(
|
|
692
702
|
message: Draft14Message,
|
|
693
|
-
_direction:
|
|
703
|
+
_direction: 'inbound' | 'outbound',
|
|
694
704
|
sideEffects: SideEffect[],
|
|
695
705
|
): TransitionResult<Draft14MessageType> {
|
|
696
|
-
const err = this.requireReady(message.type)
|
|
697
|
-
if (err) return { ok: false, violation: err }
|
|
706
|
+
const err = this.requireReady(message.type)
|
|
707
|
+
if (err) return { ok: false, violation: err }
|
|
698
708
|
|
|
699
|
-
const ok = message as import(
|
|
700
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
701
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
709
|
+
const ok = message as import('./types.js').Draft14FetchOk
|
|
710
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
711
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
702
712
|
|
|
703
|
-
const existing = this._fetches.get(ok.request_id)
|
|
713
|
+
const existing = this._fetches.get(ok.request_id)
|
|
704
714
|
if (!existing) {
|
|
705
715
|
return {
|
|
706
716
|
ok: false,
|
|
707
717
|
violation: violation(
|
|
708
|
-
|
|
718
|
+
'UNKNOWN_REQUEST_ID',
|
|
709
719
|
`No fetch with request ID ${ok.request_id}`,
|
|
710
720
|
this._phase,
|
|
711
721
|
message.type,
|
|
712
722
|
),
|
|
713
|
-
}
|
|
723
|
+
}
|
|
714
724
|
}
|
|
715
|
-
if (existing.phase !==
|
|
725
|
+
if (existing.phase !== 'pending') {
|
|
716
726
|
return {
|
|
717
727
|
ok: false,
|
|
718
728
|
violation: violation(
|
|
719
|
-
|
|
729
|
+
'STATE_VIOLATION',
|
|
720
730
|
`Fetch ${ok.request_id} is ${existing.phase}, not pending`,
|
|
721
731
|
this._phase,
|
|
722
732
|
message.type,
|
|
723
733
|
),
|
|
724
|
-
}
|
|
734
|
+
}
|
|
725
735
|
}
|
|
726
736
|
|
|
727
|
-
this._fetches.set(ok.request_id, { ...existing, phase:
|
|
728
|
-
sideEffects.push({ type:
|
|
729
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
737
|
+
this._fetches.set(ok.request_id, { ...existing, phase: 'active' })
|
|
738
|
+
sideEffects.push({ type: 'fetch-activated', requestId: ok.request_id })
|
|
739
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
730
740
|
}
|
|
731
741
|
|
|
732
742
|
private handleFetchError(
|
|
733
743
|
message: Draft14Message,
|
|
734
|
-
_direction:
|
|
744
|
+
_direction: 'inbound' | 'outbound',
|
|
735
745
|
sideEffects: SideEffect[],
|
|
736
746
|
): TransitionResult<Draft14MessageType> {
|
|
737
|
-
const err = this.requireReady(message.type)
|
|
738
|
-
if (err) return { ok: false, violation: err }
|
|
747
|
+
const err = this.requireReady(message.type)
|
|
748
|
+
if (err) return { ok: false, violation: err }
|
|
739
749
|
|
|
740
|
-
const fetchErr = message as import(
|
|
741
|
-
const idErr = this.checkKnownRequestId(fetchErr.request_id, message.type)
|
|
742
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
750
|
+
const fetchErr = message as import('./types.js').Draft14FetchError
|
|
751
|
+
const idErr = this.checkKnownRequestId(fetchErr.request_id, message.type)
|
|
752
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
743
753
|
|
|
744
|
-
const existing = this._fetches.get(fetchErr.request_id)
|
|
754
|
+
const existing = this._fetches.get(fetchErr.request_id)
|
|
745
755
|
if (!existing) {
|
|
746
756
|
return {
|
|
747
757
|
ok: false,
|
|
748
758
|
violation: violation(
|
|
749
|
-
|
|
759
|
+
'UNKNOWN_REQUEST_ID',
|
|
750
760
|
`No fetch with request ID ${fetchErr.request_id}`,
|
|
751
761
|
this._phase,
|
|
752
762
|
message.type,
|
|
753
763
|
),
|
|
754
|
-
}
|
|
764
|
+
}
|
|
755
765
|
}
|
|
756
|
-
if (existing.phase !==
|
|
766
|
+
if (existing.phase !== 'pending') {
|
|
757
767
|
return {
|
|
758
768
|
ok: false,
|
|
759
769
|
violation: violation(
|
|
760
|
-
|
|
770
|
+
'STATE_VIOLATION',
|
|
761
771
|
`Fetch ${fetchErr.request_id} is ${existing.phase}, not pending`,
|
|
762
772
|
this._phase,
|
|
763
773
|
message.type,
|
|
764
774
|
),
|
|
765
|
-
}
|
|
775
|
+
}
|
|
766
776
|
}
|
|
767
777
|
|
|
768
|
-
this._fetches.set(fetchErr.request_id, { ...existing, phase:
|
|
778
|
+
this._fetches.set(fetchErr.request_id, { ...existing, phase: 'error' })
|
|
769
779
|
sideEffects.push({
|
|
770
|
-
type:
|
|
780
|
+
type: 'fetch-ended',
|
|
771
781
|
requestId: fetchErr.request_id,
|
|
772
782
|
reason: fetchErr.reason_phrase,
|
|
773
|
-
})
|
|
774
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
783
|
+
})
|
|
784
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
775
785
|
}
|
|
776
786
|
|
|
777
787
|
private handleFetchCancel(
|
|
778
788
|
message: Draft14Message,
|
|
779
|
-
_direction:
|
|
789
|
+
_direction: 'inbound' | 'outbound',
|
|
780
790
|
sideEffects: SideEffect[],
|
|
781
791
|
): TransitionResult<Draft14MessageType> {
|
|
782
|
-
const err = this.requireReady(message.type)
|
|
783
|
-
if (err) return { ok: false, violation: err }
|
|
792
|
+
const err = this.requireReady(message.type)
|
|
793
|
+
if (err) return { ok: false, violation: err }
|
|
784
794
|
|
|
785
|
-
const cancel = message as import(
|
|
786
|
-
const idErr = this.checkKnownRequestId(cancel.request_id, message.type)
|
|
787
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
795
|
+
const cancel = message as import('./types.js').Draft14FetchCancel
|
|
796
|
+
const idErr = this.checkKnownRequestId(cancel.request_id, message.type)
|
|
797
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
788
798
|
|
|
789
|
-
const existing = this._fetches.get(cancel.request_id)
|
|
799
|
+
const existing = this._fetches.get(cancel.request_id)
|
|
790
800
|
if (!existing) {
|
|
791
801
|
return {
|
|
792
802
|
ok: false,
|
|
793
803
|
violation: violation(
|
|
794
|
-
|
|
804
|
+
'UNKNOWN_REQUEST_ID',
|
|
795
805
|
`No fetch with request ID ${cancel.request_id}`,
|
|
796
806
|
this._phase,
|
|
797
807
|
message.type,
|
|
798
808
|
),
|
|
799
|
-
}
|
|
809
|
+
}
|
|
800
810
|
}
|
|
801
811
|
|
|
802
|
-
this._fetches.set(cancel.request_id, { ...existing, phase:
|
|
803
|
-
sideEffects.push({
|
|
804
|
-
|
|
812
|
+
this._fetches.set(cancel.request_id, { ...existing, phase: 'cancelled' })
|
|
813
|
+
sideEffects.push({
|
|
814
|
+
type: 'fetch-ended',
|
|
815
|
+
requestId: cancel.request_id,
|
|
816
|
+
reason: 'cancelled',
|
|
817
|
+
})
|
|
818
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
805
819
|
}
|
|
806
820
|
|
|
807
821
|
// ─── Publish namespace lifecycle ──────────────────────────────────────────────
|
|
@@ -810,61 +824,57 @@ export class Draft14SessionFSM {
|
|
|
810
824
|
message: Draft14Message,
|
|
811
825
|
sideEffects: SideEffect[],
|
|
812
826
|
): TransitionResult<Draft14MessageType> {
|
|
813
|
-
const err = this.requireReady(message.type)
|
|
814
|
-
if (err) return { ok: false, violation: err }
|
|
815
|
-
const pn = message as import(
|
|
816
|
-
const dupErr = this.checkDuplicateRequestId(pn.request_id, message.type)
|
|
817
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
818
|
-
this._requestIds.add(pn.request_id)
|
|
819
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
827
|
+
const err = this.requireReady(message.type)
|
|
828
|
+
if (err) return { ok: false, violation: err }
|
|
829
|
+
const pn = message as import('./types.js').Draft14PublishNamespace
|
|
830
|
+
const dupErr = this.checkDuplicateRequestId(pn.request_id, message.type)
|
|
831
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
832
|
+
this._requestIds.add(pn.request_id)
|
|
833
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
820
834
|
}
|
|
821
835
|
|
|
822
836
|
private handlePublishNamespaceOk(
|
|
823
837
|
message: Draft14Message,
|
|
824
838
|
sideEffects: SideEffect[],
|
|
825
839
|
): TransitionResult<Draft14MessageType> {
|
|
826
|
-
const err = this.requireReady(message.type)
|
|
827
|
-
if (err) return { ok: false, violation: err }
|
|
828
|
-
const ok = message as import(
|
|
829
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
830
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
831
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
840
|
+
const err = this.requireReady(message.type)
|
|
841
|
+
if (err) return { ok: false, violation: err }
|
|
842
|
+
const ok = message as import('./types.js').Draft14PublishNamespaceOk
|
|
843
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
844
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
845
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
832
846
|
}
|
|
833
847
|
|
|
834
848
|
private handlePublishNamespaceError(
|
|
835
849
|
message: Draft14Message,
|
|
836
850
|
sideEffects: SideEffect[],
|
|
837
851
|
): TransitionResult<Draft14MessageType> {
|
|
838
|
-
const err = this.requireReady(message.type)
|
|
839
|
-
if (err) return { ok: false, violation: err }
|
|
840
|
-
const pnErr = message as import(
|
|
841
|
-
const idErr = this.checkKnownRequestId(pnErr.request_id, message.type)
|
|
842
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
843
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
852
|
+
const err = this.requireReady(message.type)
|
|
853
|
+
if (err) return { ok: false, violation: err }
|
|
854
|
+
const pnErr = message as import('./types.js').Draft14PublishNamespaceError
|
|
855
|
+
const idErr = this.checkKnownRequestId(pnErr.request_id, message.type)
|
|
856
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
857
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
844
858
|
}
|
|
845
859
|
|
|
846
860
|
private handlePublishNamespaceDone(
|
|
847
861
|
message: Draft14Message,
|
|
848
862
|
sideEffects: SideEffect[],
|
|
849
863
|
): TransitionResult<Draft14MessageType> {
|
|
850
|
-
const err = this.requireReady(message.type)
|
|
851
|
-
if (err) return { ok: false, violation: err }
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (idErr) return { ok: false, violation: idErr };
|
|
855
|
-
return { ok: true, phase: this._phase, sideEffects };
|
|
864
|
+
const err = this.requireReady(message.type)
|
|
865
|
+
if (err) return { ok: false, violation: err }
|
|
866
|
+
// PublishNamespaceDone carries only track_namespace, no request_id
|
|
867
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
856
868
|
}
|
|
857
869
|
|
|
858
870
|
private handlePublishNamespaceCancel(
|
|
859
871
|
message: Draft14Message,
|
|
860
872
|
sideEffects: SideEffect[],
|
|
861
873
|
): TransitionResult<Draft14MessageType> {
|
|
862
|
-
const err = this.requireReady(message.type)
|
|
863
|
-
if (err) return { ok: false, violation: err }
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
if (idErr) return { ok: false, violation: idErr };
|
|
867
|
-
return { ok: true, phase: this._phase, sideEffects };
|
|
874
|
+
const err = this.requireReady(message.type)
|
|
875
|
+
if (err) return { ok: false, violation: err }
|
|
876
|
+
// PublishNamespaceCancel carries track_namespace + error_code, no request_id
|
|
877
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
868
878
|
}
|
|
869
879
|
|
|
870
880
|
// ─── Subscribe namespace lifecycle ──────────────────────────────────────────────
|
|
@@ -873,47 +883,47 @@ export class Draft14SessionFSM {
|
|
|
873
883
|
message: Draft14Message,
|
|
874
884
|
sideEffects: SideEffect[],
|
|
875
885
|
): TransitionResult<Draft14MessageType> {
|
|
876
|
-
const err = this.requireReady(message.type)
|
|
877
|
-
if (err) return { ok: false, violation: err }
|
|
878
|
-
const sn = message as import(
|
|
879
|
-
const dupErr = this.checkDuplicateRequestId(sn.request_id, message.type)
|
|
880
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
881
|
-
this._requestIds.add(sn.request_id)
|
|
882
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
886
|
+
const err = this.requireReady(message.type)
|
|
887
|
+
if (err) return { ok: false, violation: err }
|
|
888
|
+
const sn = message as import('./types.js').Draft14SubscribeNamespace
|
|
889
|
+
const dupErr = this.checkDuplicateRequestId(sn.request_id, message.type)
|
|
890
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
891
|
+
this._requestIds.add(sn.request_id)
|
|
892
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
883
893
|
}
|
|
884
894
|
|
|
885
895
|
private handleSubscribeNamespaceOk(
|
|
886
896
|
message: Draft14Message,
|
|
887
897
|
sideEffects: SideEffect[],
|
|
888
898
|
): TransitionResult<Draft14MessageType> {
|
|
889
|
-
const err = this.requireReady(message.type)
|
|
890
|
-
if (err) return { ok: false, violation: err }
|
|
891
|
-
const ok = message as import(
|
|
892
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
893
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
894
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
899
|
+
const err = this.requireReady(message.type)
|
|
900
|
+
if (err) return { ok: false, violation: err }
|
|
901
|
+
const ok = message as import('./types.js').Draft14SubscribeNamespaceOk
|
|
902
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
903
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
904
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
895
905
|
}
|
|
896
906
|
|
|
897
907
|
private handleSubscribeNamespaceError(
|
|
898
908
|
message: Draft14Message,
|
|
899
909
|
sideEffects: SideEffect[],
|
|
900
910
|
): TransitionResult<Draft14MessageType> {
|
|
901
|
-
const err = this.requireReady(message.type)
|
|
902
|
-
if (err) return { ok: false, violation: err }
|
|
903
|
-
const snErr = message as import(
|
|
904
|
-
const idErr = this.checkKnownRequestId(snErr.request_id, message.type)
|
|
905
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
906
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
911
|
+
const err = this.requireReady(message.type)
|
|
912
|
+
if (err) return { ok: false, violation: err }
|
|
913
|
+
const snErr = message as import('./types.js').Draft14SubscribeNamespaceError
|
|
914
|
+
const idErr = this.checkKnownRequestId(snErr.request_id, message.type)
|
|
915
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
916
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
907
917
|
}
|
|
908
918
|
|
|
909
919
|
private handleUnsubscribeNamespace(
|
|
910
920
|
message: Draft14Message,
|
|
911
921
|
sideEffects: SideEffect[],
|
|
912
922
|
): TransitionResult<Draft14MessageType> {
|
|
913
|
-
const err = this.requireReady(message.type)
|
|
914
|
-
if (err) return { ok: false, violation: err }
|
|
923
|
+
const err = this.requireReady(message.type)
|
|
924
|
+
if (err) return { ok: false, violation: err }
|
|
915
925
|
// unsubscribe_namespace has no request_id — namespace-based
|
|
916
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
926
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
917
927
|
}
|
|
918
928
|
|
|
919
929
|
// ─── Track status lifecycle ──────────────────────────────────────────────────
|
|
@@ -922,52 +932,52 @@ export class Draft14SessionFSM {
|
|
|
922
932
|
message: Draft14Message,
|
|
923
933
|
sideEffects: SideEffect[],
|
|
924
934
|
): TransitionResult<Draft14MessageType> {
|
|
925
|
-
const err = this.requireReady(message.type)
|
|
926
|
-
if (err) return { ok: false, violation: err }
|
|
927
|
-
const ts = message as import(
|
|
928
|
-
const dupErr = this.checkDuplicateRequestId(ts.request_id, message.type)
|
|
929
|
-
if (dupErr) return { ok: false, violation: dupErr }
|
|
930
|
-
this._requestIds.add(ts.request_id)
|
|
931
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
935
|
+
const err = this.requireReady(message.type)
|
|
936
|
+
if (err) return { ok: false, violation: err }
|
|
937
|
+
const ts = message as import('./types.js').Draft14TrackStatus
|
|
938
|
+
const dupErr = this.checkDuplicateRequestId(ts.request_id, message.type)
|
|
939
|
+
if (dupErr) return { ok: false, violation: dupErr }
|
|
940
|
+
this._requestIds.add(ts.request_id)
|
|
941
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
932
942
|
}
|
|
933
943
|
|
|
934
944
|
private handleTrackStatusOk(
|
|
935
945
|
message: Draft14Message,
|
|
936
946
|
sideEffects: SideEffect[],
|
|
937
947
|
): TransitionResult<Draft14MessageType> {
|
|
938
|
-
const err = this.requireReady(message.type)
|
|
939
|
-
if (err) return { ok: false, violation: err }
|
|
940
|
-
const ok = message as import(
|
|
941
|
-
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
942
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
943
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
948
|
+
const err = this.requireReady(message.type)
|
|
949
|
+
if (err) return { ok: false, violation: err }
|
|
950
|
+
const ok = message as import('./types.js').Draft14TrackStatusOk
|
|
951
|
+
const idErr = this.checkKnownRequestId(ok.request_id, message.type)
|
|
952
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
953
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
944
954
|
}
|
|
945
955
|
|
|
946
956
|
private handleTrackStatusError(
|
|
947
957
|
message: Draft14Message,
|
|
948
958
|
sideEffects: SideEffect[],
|
|
949
959
|
): TransitionResult<Draft14MessageType> {
|
|
950
|
-
const err = this.requireReady(message.type)
|
|
951
|
-
if (err) return { ok: false, violation: err }
|
|
952
|
-
const tsErr = message as import(
|
|
953
|
-
const idErr = this.checkKnownRequestId(tsErr.request_id, message.type)
|
|
954
|
-
if (idErr) return { ok: false, violation: idErr }
|
|
955
|
-
return { ok: true, phase: this._phase, sideEffects }
|
|
960
|
+
const err = this.requireReady(message.type)
|
|
961
|
+
if (err) return { ok: false, violation: err }
|
|
962
|
+
const tsErr = message as import('./types.js').Draft14TrackStatusError
|
|
963
|
+
const idErr = this.checkKnownRequestId(tsErr.request_id, message.type)
|
|
964
|
+
if (idErr) return { ok: false, violation: idErr }
|
|
965
|
+
return { ok: true, phase: this._phase, sideEffects }
|
|
956
966
|
}
|
|
957
967
|
|
|
958
968
|
// ─── Generic ready-phase handler ──────────────────────────────────────────────
|
|
959
969
|
|
|
960
970
|
private handleReadyPhaseMessage(message: Draft14Message): TransitionResult<Draft14MessageType> {
|
|
961
|
-
const err = this.requireReady(message.type)
|
|
962
|
-
if (err) return { ok: false, violation: err }
|
|
963
|
-
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
971
|
+
const err = this.requireReady(message.type)
|
|
972
|
+
if (err) return { ok: false, violation: err }
|
|
973
|
+
return { ok: true, phase: this._phase, sideEffects: [] }
|
|
964
974
|
}
|
|
965
975
|
|
|
966
976
|
reset(): void {
|
|
967
|
-
this._phase =
|
|
968
|
-
this._subscriptions.clear()
|
|
969
|
-
this._publishes.clear()
|
|
970
|
-
this._fetches.clear()
|
|
971
|
-
this._requestIds.clear()
|
|
977
|
+
this._phase = 'idle'
|
|
978
|
+
this._subscriptions.clear()
|
|
979
|
+
this._publishes.clear()
|
|
980
|
+
this._fetches.clear()
|
|
981
|
+
this._requestIds.clear()
|
|
972
982
|
}
|
|
973
983
|
}
|