@moqtap/codec 0.4.2 → 0.5.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/dist/chunk-A33U4WVS.cjs +1993 -0
- package/dist/chunk-A655PHFX.cjs +446 -0
- package/dist/{chunk-UJU63EFY.cjs → chunk-BAG6YWCP.cjs} +2 -3
- package/dist/{chunk-2UTVXWFF.js → chunk-COWGGUL4.js} +2 -3
- package/dist/{chunk-4IVZZOY3.js → chunk-CXFAQKL3.js} +2 -3
- package/dist/chunk-DLSGOZ6P.js +1993 -0
- package/dist/{chunk-SDWRXXOO.cjs → chunk-HO44OANK.cjs} +2 -3
- package/dist/{chunk-Q46ZURKX.js → chunk-HYI6LFMD.js} +2 -3
- package/dist/chunk-IWYKDCXP.js +446 -0
- package/dist/{chunk-WPFBK7UX.cjs → chunk-JB5VDJ2V.cjs} +4 -6
- package/dist/{chunk-2SGE5GMU.cjs → chunk-JTRU76JN.cjs} +4 -4
- package/dist/{chunk-TKBYPIN7.js → chunk-NZ7GYEMT.js} +4 -6
- package/dist/{chunk-5O764MBD.cjs → chunk-OGZTQA24.cjs} +6 -6
- package/dist/{chunk-76774UFF.js → chunk-Q5D6OW6Q.js} +4 -4
- package/dist/{chunk-XS77RBY7.js → chunk-RZ3UML47.js} +2 -4
- package/dist/{chunk-3XKO6PCR.cjs → chunk-SIYYMFCC.cjs} +2 -3
- package/dist/{chunk-Z6U26IRI.js → chunk-UP4XPH4J.js} +6 -6
- package/dist/{chunk-OKG2HT25.cjs → chunk-WMJVSBBE.cjs} +2 -4
- package/dist/{codec-CewFCsEy.d.ts → codec-3ZgEK-Yj.d.ts} +1 -1
- package/dist/{codec-C-Yk2RNa.d.ts → codec-6s9d5yZm.d.ts} +2 -2
- package/dist/{codec-w8z2j_uo.d.cts → codec-B6ouUJrm.d.cts} +2 -2
- package/dist/{codec-3Ppy_5Gd.d.ts → codec-BhW5CGZq.d.ts} +1 -1
- package/dist/{codec-wtt6WF1K.d.ts → codec-Bp4HPezr.d.ts} +1 -1
- package/dist/{codec-BB0U8MF2.d.cts → codec-BsSNjvB3.d.cts} +2 -2
- package/dist/codec-BvSmr-Jp.d.ts +30 -0
- package/dist/{codec-BU0gaSZm.d.ts → codec-BvzKRqiP.d.ts} +2 -2
- package/dist/{codec-D8REY9TB.d.ts → codec-BzRIVIzS.d.ts} +2 -2
- package/dist/{codec-DDUBJGn0.d.cts → codec-C17JvsEU.d.cts} +1 -1
- package/dist/{codec-CpBWhz9f.d.cts → codec-CBLLytyP.d.cts} +2 -2
- package/dist/{codec-DNfml-se.d.ts → codec-Cbv3sG2y.d.ts} +2 -2
- package/dist/{codec-R9TJCq-E.d.cts → codec-CzBmYisK.d.cts} +1 -1
- package/dist/{codec-CFWQY9Lj.d.cts → codec-D2YMCU11.d.cts} +1 -1
- package/dist/{codec-BN6jk0Td.d.cts → codec-D5Ss123f.d.cts} +2 -2
- package/dist/{codec-j819z_hL.d.cts → codec-DS-As0Pp.d.cts} +1 -1
- package/dist/{codec-D2QVSH9D.d.ts → codec-D_9X0i31.d.ts} +2 -2
- package/dist/{codec-z6_2PSzi.d.cts → codec-DbpLHKXN.d.cts} +2 -2
- package/dist/{codec-BHHfBTnV.d.ts → codec-DqHwYWM-.d.ts} +2 -2
- package/dist/{codec-iMEwV1UO.d.ts → codec-GayA_uN0.d.ts} +2 -2
- package/dist/codec-MxIhLDR5.d.cts +30 -0
- package/dist/{codec-DoOGjjDq.d.cts → codec-R28KwiGM.d.cts} +2 -2
- package/dist/{codec-ZwV-Njt6.d.ts → codec-Tdey03Bo.d.ts} +1 -1
- package/dist/{codec-BO5Go0Lm.d.cts → codec-yJ_axwtp.d.cts} +2 -2
- package/dist/draft07-session.d.cts +3 -3
- package/dist/draft07-session.d.ts +3 -3
- package/dist/draft07.d.cts +3 -3
- package/dist/draft07.d.ts +3 -3
- package/dist/draft08-session.d.cts +3 -3
- package/dist/draft08-session.d.ts +3 -3
- package/dist/draft08.d.cts +4 -4
- package/dist/draft08.d.ts +4 -4
- package/dist/draft09-session.d.cts +3 -3
- package/dist/draft09-session.d.ts +3 -3
- package/dist/draft09.d.cts +4 -4
- package/dist/draft09.d.ts +4 -4
- package/dist/draft10-session.d.cts +3 -3
- package/dist/draft10-session.d.ts +3 -3
- package/dist/draft10.d.cts +4 -4
- package/dist/draft10.d.ts +4 -4
- package/dist/draft11-session.d.cts +4 -4
- package/dist/draft11-session.d.ts +4 -4
- package/dist/draft11.cjs +2 -2
- package/dist/draft11.d.cts +6 -6
- package/dist/draft11.d.ts +6 -6
- package/dist/draft11.js +1 -1
- package/dist/draft12-session.d.cts +4 -4
- package/dist/draft12-session.d.ts +4 -4
- package/dist/draft12.cjs +2 -2
- package/dist/draft12.d.cts +6 -6
- package/dist/draft12.d.ts +6 -6
- package/dist/draft12.js +1 -1
- package/dist/draft13-session.d.cts +4 -4
- package/dist/draft13-session.d.ts +4 -4
- package/dist/draft13.cjs +2 -2
- package/dist/draft13.d.cts +6 -6
- package/dist/draft13.d.ts +6 -6
- package/dist/draft13.js +1 -1
- package/dist/draft14-session.d.cts +4 -4
- package/dist/draft14-session.d.ts +4 -4
- package/dist/draft14.cjs +2 -2
- package/dist/draft14.d.cts +6 -6
- package/dist/draft14.d.ts +6 -6
- package/dist/draft14.js +1 -1
- package/dist/draft15-session.d.cts +4 -4
- package/dist/draft15-session.d.ts +4 -4
- package/dist/draft15.cjs +2 -2
- package/dist/draft15.d.cts +6 -6
- package/dist/draft15.d.ts +6 -6
- package/dist/draft15.js +1 -1
- package/dist/draft16-session.d.cts +4 -4
- package/dist/draft16-session.d.ts +4 -4
- package/dist/draft16.cjs +2 -2
- package/dist/draft16.d.cts +6 -6
- package/dist/draft16.d.ts +6 -6
- package/dist/draft16.js +1 -1
- package/dist/draft17-session.d.cts +4 -4
- package/dist/draft17-session.d.ts +4 -4
- package/dist/draft17.cjs +2 -2
- package/dist/draft17.d.cts +5 -5
- package/dist/draft17.d.ts +5 -5
- package/dist/draft17.js +1 -1
- package/dist/draft18-session.cjs +8 -0
- package/dist/draft18-session.d.cts +50 -0
- package/dist/draft18-session.d.ts +50 -0
- package/dist/draft18-session.js +8 -0
- package/dist/draft18.cjs +179 -0
- package/dist/draft18.d.cts +126 -0
- package/dist/draft18.d.ts +126 -0
- package/dist/draft18.js +179 -0
- package/dist/index.cjs +21 -15
- package/dist/index.d.cts +26 -21
- package/dist/index.d.ts +26 -21
- package/dist/index.js +14 -8
- package/dist/{session-types-X-IYEGoG.d.ts → session-types-DBpCRtrL.d.ts} +1 -1
- package/dist/{session-types-C7UONex5.d.cts → session-types-sBaTRwDh.d.cts} +1 -1
- package/dist/session.cjs +5 -1
- package/dist/session.d.cts +12 -10
- package/dist/session.d.ts +12 -10
- package/dist/session.js +5 -1
- package/dist/{types-DuyE0ygT.d.cts → types-Bio-91Rt.d.cts} +1 -1
- package/dist/{types-DuyE0ygT.d.ts → types-Bio-91Rt.d.ts} +1 -1
- package/dist/{types-DuxfOYiO.d.cts → types-CPaYBsMG.d.cts} +1 -1
- package/dist/{types-DuxfOYiO.d.ts → types-CPaYBsMG.d.ts} +1 -1
- package/dist/{types-DrQpTW1G.d.cts → types-CXlSn0I9.d.cts} +1 -1
- package/dist/{types-DrQpTW1G.d.ts → types-CXlSn0I9.d.ts} +1 -1
- package/dist/{types--Q7_DcvP.d.cts → types-Ck45Ex5S.d.cts} +1 -1
- package/dist/{types--Q7_DcvP.d.ts → types-Ck45Ex5S.d.ts} +1 -1
- package/dist/{types-BFpFNM_m.d.cts → types-Cm9fjeYC.d.cts} +1 -1
- package/dist/{types-BFpFNM_m.d.ts → types-Cm9fjeYC.d.ts} +1 -1
- package/dist/{types-DuIO7cWK.d.cts → types-CnBuq68e.d.cts} +1 -1
- package/dist/{types-DuIO7cWK.d.ts → types-CnBuq68e.d.ts} +1 -1
- package/dist/types-Cqp10JtB.d.cts +249 -0
- package/dist/types-Cqp10JtB.d.ts +249 -0
- package/dist/{types-Br7fm2mf.d.cts → types-D9eiB_9A.d.cts} +2 -2
- package/dist/{types-Br7fm2mf.d.ts → types-D9eiB_9A.d.ts} +2 -2
- package/dist/{types--FGt0sAp.d.cts → types-DjmrHp_s.d.cts} +1 -1
- package/dist/{types--FGt0sAp.d.ts → types-DjmrHp_s.d.ts} +1 -1
- package/package.json +14 -2
- package/src/core/types.ts +13 -1
- package/src/drafts/draft11/codec.ts +2 -3
- package/src/drafts/draft11/types.ts +1 -1
- package/src/drafts/draft12/codec.ts +4 -6
- package/src/drafts/draft12/types.ts +1 -1
- package/src/drafts/draft13/codec.ts +2 -3
- package/src/drafts/draft13/types.ts +1 -1
- package/src/drafts/draft14/codec.ts +4 -4
- package/src/drafts/draft14/types.ts +1 -1
- package/src/drafts/draft15/codec.ts +2 -4
- package/src/drafts/draft15/types.ts +1 -1
- package/src/drafts/draft16/codec.ts +2 -3
- package/src/drafts/draft16/types.ts +1 -1
- package/src/drafts/draft17/codec.ts +6 -6
- package/src/drafts/draft17/error-codes.ts +72 -0
- package/src/drafts/draft17/types.ts +2 -2
- package/src/drafts/draft18/codec.ts +1310 -0
- package/src/drafts/draft18/data-streams.ts +996 -0
- package/src/drafts/draft18/error-codes.ts +75 -0
- package/src/drafts/draft18/index.ts +122 -0
- package/src/drafts/draft18/messages.ts +56 -0
- package/src/drafts/draft18/rules.ts +85 -0
- package/src/drafts/draft18/session-fsm.ts +451 -0
- package/src/drafts/draft18/session.ts +15 -0
- package/src/drafts/draft18/types.ts +347 -0
- package/src/index.ts +9 -1
- package/src/session.ts +4 -0
|
@@ -438,8 +438,7 @@ function encodeParams(params, w) {
|
|
|
438
438
|
const tmpW = new (0, _chunkJL3NEW3Ocjs.BufferWriter)(64);
|
|
439
439
|
tmpW.writeVarInt(at.alias_type);
|
|
440
440
|
tmpW.writeVarInt(at.token_type);
|
|
441
|
-
|
|
442
|
-
tmpW.writeBytes(tokenBytes);
|
|
441
|
+
tmpW.writeBytes(at.token_value);
|
|
443
442
|
const raw = tmpW.finish();
|
|
444
443
|
w.writeVarInt(raw.byteLength);
|
|
445
444
|
w.writeBytes(raw);
|
|
@@ -501,7 +500,7 @@ function decodeParams(r) {
|
|
|
501
500
|
result.authorization_token = {
|
|
502
501
|
alias_type,
|
|
503
502
|
token_type,
|
|
504
|
-
token_value:
|
|
503
|
+
token_value: tokenBytes
|
|
505
504
|
};
|
|
506
505
|
} else {
|
|
507
506
|
const bytes = r.readBytes(length);
|
|
@@ -888,8 +888,7 @@ function encodeAuthorizationToken(token, w) {
|
|
|
888
888
|
}
|
|
889
889
|
if (aliasType === 1 || aliasType === 3) {
|
|
890
890
|
w.writeVarInt(token.token_type);
|
|
891
|
-
|
|
892
|
-
w.writeBytes(tokenBytes);
|
|
891
|
+
w.writeBytes(token.token_value);
|
|
893
892
|
}
|
|
894
893
|
}
|
|
895
894
|
function decodeAuthorizationToken(r) {
|
|
@@ -902,7 +901,7 @@ function decodeAuthorizationToken(r) {
|
|
|
902
901
|
if (aliasType === 1 || aliasType === 3) {
|
|
903
902
|
result.token_type = r.readVarInt();
|
|
904
903
|
const tokenBytes = r.readBytesView(r.remaining);
|
|
905
|
-
result.
|
|
904
|
+
result.token_value = new Uint8Array(tokenBytes);
|
|
906
905
|
}
|
|
907
906
|
return result;
|
|
908
907
|
}
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
// src/drafts/draft18/rules.ts
|
|
2
|
+
var CONTROL_MESSAGES = /* @__PURE__ */ new Set([
|
|
3
|
+
"setup",
|
|
4
|
+
"subscribe",
|
|
5
|
+
"subscribe_ok",
|
|
6
|
+
"request_update",
|
|
7
|
+
"publish",
|
|
8
|
+
"publish_done",
|
|
9
|
+
"publish_namespace",
|
|
10
|
+
"namespace",
|
|
11
|
+
"namespace_done",
|
|
12
|
+
"subscribe_namespace",
|
|
13
|
+
"subscribe_tracks",
|
|
14
|
+
"publish_blocked",
|
|
15
|
+
"fetch",
|
|
16
|
+
"fetch_ok",
|
|
17
|
+
"track_status",
|
|
18
|
+
"request_ok",
|
|
19
|
+
"request_error",
|
|
20
|
+
"goaway"
|
|
21
|
+
]);
|
|
22
|
+
var CLIENT_ONLY_MESSAGES = /* @__PURE__ */ new Set();
|
|
23
|
+
var SERVER_ONLY_MESSAGES = /* @__PURE__ */ new Set();
|
|
24
|
+
var BIDIRECTIONAL_MESSAGES = /* @__PURE__ */ new Set([
|
|
25
|
+
"setup",
|
|
26
|
+
"subscribe",
|
|
27
|
+
"subscribe_ok",
|
|
28
|
+
"request_update",
|
|
29
|
+
"publish",
|
|
30
|
+
"publish_done",
|
|
31
|
+
"publish_namespace",
|
|
32
|
+
"namespace",
|
|
33
|
+
"namespace_done",
|
|
34
|
+
"subscribe_namespace",
|
|
35
|
+
"subscribe_tracks",
|
|
36
|
+
"publish_blocked",
|
|
37
|
+
"fetch",
|
|
38
|
+
"fetch_ok",
|
|
39
|
+
"track_status",
|
|
40
|
+
"request_ok",
|
|
41
|
+
"request_error",
|
|
42
|
+
"goaway"
|
|
43
|
+
]);
|
|
44
|
+
function getLegalOutgoing(phase, _role) {
|
|
45
|
+
const legal = /* @__PURE__ */ new Set();
|
|
46
|
+
switch (phase) {
|
|
47
|
+
case "idle":
|
|
48
|
+
legal.add("setup");
|
|
49
|
+
break;
|
|
50
|
+
case "setup":
|
|
51
|
+
legal.add("setup");
|
|
52
|
+
break;
|
|
53
|
+
case "ready": {
|
|
54
|
+
for (const msg of BIDIRECTIONAL_MESSAGES) {
|
|
55
|
+
legal.add(msg);
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "draining":
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return legal;
|
|
63
|
+
}
|
|
64
|
+
function getLegalIncoming(phase, role) {
|
|
65
|
+
const remoteRole = role === "client" ? "server" : "client";
|
|
66
|
+
return getLegalOutgoing(phase, remoteRole);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/drafts/draft18/session-fsm.ts
|
|
70
|
+
function violation(code, message, currentPhase, offendingMessage) {
|
|
71
|
+
return { code, message, currentPhase, offendingMessage };
|
|
72
|
+
}
|
|
73
|
+
var Draft18SessionFSM = class {
|
|
74
|
+
_phase = "idle";
|
|
75
|
+
_role;
|
|
76
|
+
_setupDirection = null;
|
|
77
|
+
_subscriptions = /* @__PURE__ */ new Map();
|
|
78
|
+
_publishes = /* @__PURE__ */ new Map();
|
|
79
|
+
_fetches = /* @__PURE__ */ new Map();
|
|
80
|
+
_requestIds = /* @__PURE__ */ new Set();
|
|
81
|
+
_pendingSubscribes = [];
|
|
82
|
+
_pendingPublishes = [];
|
|
83
|
+
_pendingFetches = [];
|
|
84
|
+
constructor(role) {
|
|
85
|
+
this._role = role;
|
|
86
|
+
}
|
|
87
|
+
get phase() {
|
|
88
|
+
return this._phase;
|
|
89
|
+
}
|
|
90
|
+
get role() {
|
|
91
|
+
return this._role;
|
|
92
|
+
}
|
|
93
|
+
get subscriptions() {
|
|
94
|
+
return this._subscriptions;
|
|
95
|
+
}
|
|
96
|
+
get announces() {
|
|
97
|
+
return /* @__PURE__ */ new Map();
|
|
98
|
+
}
|
|
99
|
+
get publishes() {
|
|
100
|
+
return this._publishes;
|
|
101
|
+
}
|
|
102
|
+
get fetches() {
|
|
103
|
+
return this._fetches;
|
|
104
|
+
}
|
|
105
|
+
get legalOutgoing() {
|
|
106
|
+
return getLegalOutgoing(this._phase, this._role);
|
|
107
|
+
}
|
|
108
|
+
get legalIncoming() {
|
|
109
|
+
return getLegalIncoming(this._phase, this._role);
|
|
110
|
+
}
|
|
111
|
+
checkDuplicateRequestId(requestId, msgType) {
|
|
112
|
+
if (this._requestIds.has(requestId)) {
|
|
113
|
+
return violation(
|
|
114
|
+
"DUPLICATE_REQUEST_ID",
|
|
115
|
+
`Request ID ${requestId} already in use`,
|
|
116
|
+
this._phase,
|
|
117
|
+
msgType
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
validateOutgoing(message) {
|
|
123
|
+
if (!this.legalOutgoing.has(message.type)) {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
violation: violation(
|
|
127
|
+
this._phase === "idle" || this._phase === "setup" ? "MESSAGE_BEFORE_SETUP" : "UNEXPECTED_MESSAGE",
|
|
128
|
+
`Cannot send ${message.type} in phase ${this._phase}`,
|
|
129
|
+
this._phase,
|
|
130
|
+
message.type
|
|
131
|
+
)
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return { ok: true };
|
|
135
|
+
}
|
|
136
|
+
receive(message) {
|
|
137
|
+
return this.applyTransition(message, "inbound");
|
|
138
|
+
}
|
|
139
|
+
send(message) {
|
|
140
|
+
return this.applyTransition(message, "outbound");
|
|
141
|
+
}
|
|
142
|
+
applyTransition(message, direction) {
|
|
143
|
+
const sideEffects = [];
|
|
144
|
+
switch (message.type) {
|
|
145
|
+
case "setup":
|
|
146
|
+
return this.handleSetup(direction);
|
|
147
|
+
case "goaway":
|
|
148
|
+
return this.handleGoAway(message, sideEffects);
|
|
149
|
+
case "subscribe":
|
|
150
|
+
return this.handleSubscribe(message, sideEffects);
|
|
151
|
+
case "subscribe_ok":
|
|
152
|
+
return this.handleSubscribeOk(message, sideEffects);
|
|
153
|
+
case "request_update":
|
|
154
|
+
return this.handleRequestUpdate(message, sideEffects);
|
|
155
|
+
case "publish":
|
|
156
|
+
return this.handlePublish(message, sideEffects);
|
|
157
|
+
case "publish_done":
|
|
158
|
+
return this.handlePublishDone(message, sideEffects);
|
|
159
|
+
case "fetch":
|
|
160
|
+
return this.handleFetch(message, sideEffects);
|
|
161
|
+
case "fetch_ok":
|
|
162
|
+
return this.handleFetchOk(sideEffects);
|
|
163
|
+
case "request_ok":
|
|
164
|
+
return this.handleRequestOk(sideEffects);
|
|
165
|
+
case "request_error":
|
|
166
|
+
return this.handleRequestError(sideEffects);
|
|
167
|
+
default:
|
|
168
|
+
return this.handleReadyPhaseMessage(message);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
handleSetup(direction) {
|
|
172
|
+
if (this._phase === "idle") {
|
|
173
|
+
this._setupDirection = direction;
|
|
174
|
+
this._phase = "setup";
|
|
175
|
+
return { ok: true, phase: this._phase, sideEffects: [] };
|
|
176
|
+
}
|
|
177
|
+
if (this._phase === "setup") {
|
|
178
|
+
if (direction === this._setupDirection) {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
violation: violation(
|
|
182
|
+
"SETUP_VIOLATION",
|
|
183
|
+
`Second SETUP must be ${this._setupDirection === "inbound" ? "outbound" : "inbound"}, got ${direction}`,
|
|
184
|
+
this._phase,
|
|
185
|
+
"setup"
|
|
186
|
+
)
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
this._phase = "ready";
|
|
190
|
+
return {
|
|
191
|
+
ok: true,
|
|
192
|
+
phase: this._phase,
|
|
193
|
+
sideEffects: [{ type: "session-ready" }]
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
violation: violation(
|
|
199
|
+
"SETUP_VIOLATION",
|
|
200
|
+
"SETUP not valid in current phase",
|
|
201
|
+
this._phase,
|
|
202
|
+
"setup"
|
|
203
|
+
)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
handleGoAway(message, sideEffects) {
|
|
207
|
+
if (this._phase !== "ready" && this._phase !== "draining") {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
violation: violation(
|
|
211
|
+
"UNEXPECTED_MESSAGE",
|
|
212
|
+
`GOAWAY not valid in phase ${this._phase}`,
|
|
213
|
+
this._phase,
|
|
214
|
+
"goaway"
|
|
215
|
+
)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
this._phase = "draining";
|
|
219
|
+
const goaway = message;
|
|
220
|
+
sideEffects.push({
|
|
221
|
+
type: "session-draining",
|
|
222
|
+
goAwayUri: goaway.new_session_uri
|
|
223
|
+
});
|
|
224
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
225
|
+
}
|
|
226
|
+
requireReady(msgType) {
|
|
227
|
+
if (this._phase !== "ready" && this._phase !== "draining") {
|
|
228
|
+
return violation(
|
|
229
|
+
this._phase === "idle" || this._phase === "setup" ? "MESSAGE_BEFORE_SETUP" : "UNEXPECTED_MESSAGE",
|
|
230
|
+
`${msgType} requires ready phase, current: ${this._phase}`,
|
|
231
|
+
this._phase,
|
|
232
|
+
msgType
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
handleSubscribe(message, sideEffects) {
|
|
238
|
+
const err = this.requireReady(message.type);
|
|
239
|
+
if (err) return { ok: false, violation: err };
|
|
240
|
+
const sub = message;
|
|
241
|
+
const dupErr = this.checkDuplicateRequestId(sub.request_id, message.type);
|
|
242
|
+
if (dupErr) return { ok: false, violation: dupErr };
|
|
243
|
+
this._requestIds.add(sub.request_id);
|
|
244
|
+
this._subscriptions.set(sub.request_id, {
|
|
245
|
+
subscribeId: sub.request_id,
|
|
246
|
+
phase: "pending",
|
|
247
|
+
trackNamespace: sub.track_namespace,
|
|
248
|
+
trackName: sub.track_name
|
|
249
|
+
});
|
|
250
|
+
this._pendingSubscribes.push(sub.request_id);
|
|
251
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
252
|
+
}
|
|
253
|
+
handleSubscribeOk(_message, sideEffects) {
|
|
254
|
+
const err = this.requireReady("subscribe_ok");
|
|
255
|
+
if (err) return { ok: false, violation: err };
|
|
256
|
+
const requestId = this._pendingSubscribes.shift();
|
|
257
|
+
if (requestId === void 0) {
|
|
258
|
+
return {
|
|
259
|
+
ok: false,
|
|
260
|
+
violation: violation(
|
|
261
|
+
"UNEXPECTED_MESSAGE",
|
|
262
|
+
"SUBSCRIBE_OK with no pending subscribe",
|
|
263
|
+
this._phase,
|
|
264
|
+
"subscribe_ok"
|
|
265
|
+
)
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const existing = this._subscriptions.get(requestId);
|
|
269
|
+
if (existing && existing.phase === "pending") {
|
|
270
|
+
this._subscriptions.set(requestId, { ...existing, phase: "active" });
|
|
271
|
+
sideEffects.push({
|
|
272
|
+
type: "subscription-activated",
|
|
273
|
+
subscribeId: requestId
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
277
|
+
}
|
|
278
|
+
handleRequestUpdate(message, sideEffects) {
|
|
279
|
+
const err = this.requireReady(message.type);
|
|
280
|
+
if (err) return { ok: false, violation: err };
|
|
281
|
+
const update = message;
|
|
282
|
+
const dupErr = this.checkDuplicateRequestId(update.request_id, message.type);
|
|
283
|
+
if (dupErr) return { ok: false, violation: dupErr };
|
|
284
|
+
this._requestIds.add(update.request_id);
|
|
285
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
286
|
+
}
|
|
287
|
+
handlePublish(message, sideEffects) {
|
|
288
|
+
const err = this.requireReady(message.type);
|
|
289
|
+
if (err) return { ok: false, violation: err };
|
|
290
|
+
const pub = message;
|
|
291
|
+
const dupErr = this.checkDuplicateRequestId(pub.request_id, message.type);
|
|
292
|
+
if (dupErr) return { ok: false, violation: dupErr };
|
|
293
|
+
this._requestIds.add(pub.request_id);
|
|
294
|
+
this._publishes.set(pub.request_id, {
|
|
295
|
+
requestId: pub.request_id,
|
|
296
|
+
phase: "pending"
|
|
297
|
+
});
|
|
298
|
+
this._pendingPublishes.push(pub.request_id);
|
|
299
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
300
|
+
}
|
|
301
|
+
handlePublishDone(_message, sideEffects) {
|
|
302
|
+
const err = this.requireReady("publish_done");
|
|
303
|
+
if (err) return { ok: false, violation: err };
|
|
304
|
+
for (const [reqId, pub] of this._publishes) {
|
|
305
|
+
if (pub.phase === "active") {
|
|
306
|
+
this._publishes.set(reqId, { ...pub, phase: "done" });
|
|
307
|
+
sideEffects.push({
|
|
308
|
+
type: "publish-ended",
|
|
309
|
+
requestId: reqId,
|
|
310
|
+
reason: "done"
|
|
311
|
+
});
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
316
|
+
}
|
|
317
|
+
handleFetch(message, sideEffects) {
|
|
318
|
+
const err = this.requireReady(message.type);
|
|
319
|
+
if (err) return { ok: false, violation: err };
|
|
320
|
+
const fetch = message;
|
|
321
|
+
const dupErr = this.checkDuplicateRequestId(fetch.request_id, message.type);
|
|
322
|
+
if (dupErr) return { ok: false, violation: dupErr };
|
|
323
|
+
this._requestIds.add(fetch.request_id);
|
|
324
|
+
this._fetches.set(fetch.request_id, {
|
|
325
|
+
requestId: fetch.request_id,
|
|
326
|
+
phase: "pending"
|
|
327
|
+
});
|
|
328
|
+
this._pendingFetches.push(fetch.request_id);
|
|
329
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
330
|
+
}
|
|
331
|
+
handleFetchOk(sideEffects) {
|
|
332
|
+
const err = this.requireReady("fetch_ok");
|
|
333
|
+
if (err) return { ok: false, violation: err };
|
|
334
|
+
const requestId = this._pendingFetches.shift();
|
|
335
|
+
if (requestId === void 0) {
|
|
336
|
+
return {
|
|
337
|
+
ok: false,
|
|
338
|
+
violation: violation(
|
|
339
|
+
"UNEXPECTED_MESSAGE",
|
|
340
|
+
"FETCH_OK with no pending fetch",
|
|
341
|
+
this._phase,
|
|
342
|
+
"fetch_ok"
|
|
343
|
+
)
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
const existing = this._fetches.get(requestId);
|
|
347
|
+
if (existing && existing.phase === "pending") {
|
|
348
|
+
this._fetches.set(requestId, { ...existing, phase: "active" });
|
|
349
|
+
sideEffects.push({ type: "fetch-activated", requestId });
|
|
350
|
+
}
|
|
351
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
352
|
+
}
|
|
353
|
+
handleRequestError(sideEffects) {
|
|
354
|
+
const err = this.requireReady("request_error");
|
|
355
|
+
if (err) return { ok: false, violation: err };
|
|
356
|
+
const subId = this.dequeuePending(this._pendingSubscribes, this._subscriptions);
|
|
357
|
+
if (subId !== void 0) {
|
|
358
|
+
const sub = this._subscriptions.get(subId);
|
|
359
|
+
this._subscriptions.set(subId, { ...sub, phase: "error" });
|
|
360
|
+
sideEffects.push({
|
|
361
|
+
type: "subscription-ended",
|
|
362
|
+
subscribeId: subId,
|
|
363
|
+
reason: "request_error"
|
|
364
|
+
});
|
|
365
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
366
|
+
}
|
|
367
|
+
const pubId = this.dequeuePending(this._pendingPublishes, this._publishes);
|
|
368
|
+
if (pubId !== void 0) {
|
|
369
|
+
const pub = this._publishes.get(pubId);
|
|
370
|
+
this._publishes.set(pubId, { ...pub, phase: "error" });
|
|
371
|
+
sideEffects.push({
|
|
372
|
+
type: "publish-ended",
|
|
373
|
+
requestId: pubId,
|
|
374
|
+
reason: "request_error"
|
|
375
|
+
});
|
|
376
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
377
|
+
}
|
|
378
|
+
const fetchId = this.dequeuePending(this._pendingFetches, this._fetches);
|
|
379
|
+
if (fetchId !== void 0) {
|
|
380
|
+
const f = this._fetches.get(fetchId);
|
|
381
|
+
this._fetches.set(fetchId, { ...f, phase: "error" });
|
|
382
|
+
sideEffects.push({
|
|
383
|
+
type: "fetch-ended",
|
|
384
|
+
requestId: fetchId,
|
|
385
|
+
reason: "request_error"
|
|
386
|
+
});
|
|
387
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
388
|
+
}
|
|
389
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
390
|
+
}
|
|
391
|
+
handleRequestOk(sideEffects) {
|
|
392
|
+
const err = this.requireReady("request_ok");
|
|
393
|
+
if (err) return { ok: false, violation: err };
|
|
394
|
+
const pubId = this.dequeuePending(this._pendingPublishes, this._publishes);
|
|
395
|
+
if (pubId !== void 0) {
|
|
396
|
+
const pub = this._publishes.get(pubId);
|
|
397
|
+
this._publishes.set(pubId, { ...pub, phase: "active" });
|
|
398
|
+
sideEffects.push({ type: "publish-activated", requestId: pubId });
|
|
399
|
+
}
|
|
400
|
+
return { ok: true, phase: this._phase, sideEffects };
|
|
401
|
+
}
|
|
402
|
+
dequeuePending(queue, stateMap) {
|
|
403
|
+
while (queue.length > 0) {
|
|
404
|
+
const id = queue[0];
|
|
405
|
+
const state = stateMap.get(id);
|
|
406
|
+
if (state && state.phase === "pending") {
|
|
407
|
+
queue.shift();
|
|
408
|
+
return id;
|
|
409
|
+
}
|
|
410
|
+
queue.shift();
|
|
411
|
+
}
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
handleReadyPhaseMessage(message) {
|
|
415
|
+
const err = this.requireReady(message.type);
|
|
416
|
+
if (err) return { ok: false, violation: err };
|
|
417
|
+
return { ok: true, phase: this._phase, sideEffects: [] };
|
|
418
|
+
}
|
|
419
|
+
reset() {
|
|
420
|
+
this._phase = "idle";
|
|
421
|
+
this._setupDirection = null;
|
|
422
|
+
this._subscriptions.clear();
|
|
423
|
+
this._publishes.clear();
|
|
424
|
+
this._fetches.clear();
|
|
425
|
+
this._requestIds.clear();
|
|
426
|
+
this._pendingSubscribes.length = 0;
|
|
427
|
+
this._pendingPublishes.length = 0;
|
|
428
|
+
this._pendingFetches.length = 0;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// src/drafts/draft18/session.ts
|
|
433
|
+
function createDraft18SessionState(role) {
|
|
434
|
+
return new Draft18SessionFSM(role);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export {
|
|
438
|
+
CONTROL_MESSAGES,
|
|
439
|
+
CLIENT_ONLY_MESSAGES,
|
|
440
|
+
SERVER_ONLY_MESSAGES,
|
|
441
|
+
BIDIRECTIONAL_MESSAGES,
|
|
442
|
+
getLegalOutgoing,
|
|
443
|
+
getLegalIncoming,
|
|
444
|
+
Draft18SessionFSM,
|
|
445
|
+
createDraft18SessionState
|
|
446
|
+
};
|
|
@@ -393,8 +393,7 @@ function encodeSetupParams(params, w) {
|
|
|
393
393
|
const tmpW = new (0, _chunkJL3NEW3Ocjs.BufferWriter)(64);
|
|
394
394
|
tmpW.writeVarInt(at.alias_type);
|
|
395
395
|
tmpW.writeVarInt(at.token_type);
|
|
396
|
-
|
|
397
|
-
tmpW.writeBytes(tokenBytes);
|
|
396
|
+
tmpW.writeBytes(at.token_value);
|
|
398
397
|
const raw = tmpW.finish();
|
|
399
398
|
w.writeVarInt(raw.byteLength);
|
|
400
399
|
w.writeBytes(raw);
|
|
@@ -449,7 +448,7 @@ function decodeSetupParams(r) {
|
|
|
449
448
|
result.authorization_token = {
|
|
450
449
|
alias_type,
|
|
451
450
|
token_type,
|
|
452
|
-
token_value:
|
|
451
|
+
token_value: tokenBytes
|
|
453
452
|
};
|
|
454
453
|
} else {
|
|
455
454
|
const bytes = r.readBytes(length);
|
|
@@ -480,8 +479,7 @@ function encodeParams(params, w) {
|
|
|
480
479
|
const tmpW = new (0, _chunkJL3NEW3Ocjs.BufferWriter)(64);
|
|
481
480
|
tmpW.writeVarInt(at.alias_type);
|
|
482
481
|
tmpW.writeVarInt(at.token_type);
|
|
483
|
-
|
|
484
|
-
tmpW.writeBytes(tokenBytes);
|
|
482
|
+
tmpW.writeBytes(at.token_value);
|
|
485
483
|
const raw = tmpW.finish();
|
|
486
484
|
w.writeVarInt(raw.byteLength);
|
|
487
485
|
w.writeBytes(raw);
|
|
@@ -539,7 +537,7 @@ function decodeParams(r) {
|
|
|
539
537
|
result.authorization_token = {
|
|
540
538
|
alias_type,
|
|
541
539
|
token_type,
|
|
542
|
-
token_value:
|
|
540
|
+
token_value: tokenBytes
|
|
543
541
|
};
|
|
544
542
|
} else {
|
|
545
543
|
const bytes = r.readBytes(length);
|
|
@@ -830,7 +830,7 @@ function encodeSetupParams(params, writer) {
|
|
|
830
830
|
tmpWriter.writeVarInt(tok.token_type);
|
|
831
831
|
}
|
|
832
832
|
if (tok.token_value !== void 0) {
|
|
833
|
-
tmpWriter.writeBytes(
|
|
833
|
+
tmpWriter.writeBytes(tok.token_value);
|
|
834
834
|
}
|
|
835
835
|
if (tok.token_alias !== void 0) {
|
|
836
836
|
tmpWriter.writeVarInt(tok.token_alias);
|
|
@@ -902,7 +902,7 @@ function decodeSetupParams(reader) {
|
|
|
902
902
|
tok.token_type = tokenReader.readVarInt();
|
|
903
903
|
if (tokenReader.remaining > 0) {
|
|
904
904
|
const tokenValue = tokenReader.readBytesView(tokenReader.remaining);
|
|
905
|
-
tok.token_value =
|
|
905
|
+
tok.token_value = new Uint8Array(tokenValue);
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
result.authorization_token = tok;
|
|
@@ -942,7 +942,7 @@ function encodeMessageParams(params, writer) {
|
|
|
942
942
|
tmpWriter.writeVarInt(tok.token_type);
|
|
943
943
|
}
|
|
944
944
|
if (tok.token_value !== void 0) {
|
|
945
|
-
tmpWriter.writeBytes(
|
|
945
|
+
tmpWriter.writeBytes(tok.token_value);
|
|
946
946
|
}
|
|
947
947
|
if (tok.token_alias !== void 0) {
|
|
948
948
|
tmpWriter.writeVarInt(tok.token_alias);
|
|
@@ -998,7 +998,7 @@ function decodeMessageParams(reader) {
|
|
|
998
998
|
tok.token_type = tokenReader.readVarInt();
|
|
999
999
|
if (tokenReader.remaining > 0) {
|
|
1000
1000
|
const tokenValue = tokenReader.readBytesView(tokenReader.remaining);
|
|
1001
|
-
tok.token_value =
|
|
1001
|
+
tok.token_value = new Uint8Array(tokenValue);
|
|
1002
1002
|
}
|
|
1003
1003
|
}
|
|
1004
1004
|
result.authorization_token = tok;
|
|
@@ -393,8 +393,7 @@ function encodeSetupParams(params, w) {
|
|
|
393
393
|
const tmpW = new BufferWriter(64);
|
|
394
394
|
tmpW.writeVarInt(at.alias_type);
|
|
395
395
|
tmpW.writeVarInt(at.token_type);
|
|
396
|
-
|
|
397
|
-
tmpW.writeBytes(tokenBytes);
|
|
396
|
+
tmpW.writeBytes(at.token_value);
|
|
398
397
|
const raw = tmpW.finish();
|
|
399
398
|
w.writeVarInt(raw.byteLength);
|
|
400
399
|
w.writeBytes(raw);
|
|
@@ -449,7 +448,7 @@ function decodeSetupParams(r) {
|
|
|
449
448
|
result.authorization_token = {
|
|
450
449
|
alias_type,
|
|
451
450
|
token_type,
|
|
452
|
-
token_value:
|
|
451
|
+
token_value: tokenBytes
|
|
453
452
|
};
|
|
454
453
|
} else {
|
|
455
454
|
const bytes = r.readBytes(length);
|
|
@@ -480,8 +479,7 @@ function encodeParams(params, w) {
|
|
|
480
479
|
const tmpW = new BufferWriter(64);
|
|
481
480
|
tmpW.writeVarInt(at.alias_type);
|
|
482
481
|
tmpW.writeVarInt(at.token_type);
|
|
483
|
-
|
|
484
|
-
tmpW.writeBytes(tokenBytes);
|
|
482
|
+
tmpW.writeBytes(at.token_value);
|
|
485
483
|
const raw = tmpW.finish();
|
|
486
484
|
w.writeVarInt(raw.byteLength);
|
|
487
485
|
w.writeBytes(raw);
|
|
@@ -539,7 +537,7 @@ function decodeParams(r) {
|
|
|
539
537
|
result.authorization_token = {
|
|
540
538
|
alias_type,
|
|
541
539
|
token_type,
|
|
542
|
-
token_value:
|
|
540
|
+
token_value: tokenBytes
|
|
543
541
|
};
|
|
544
542
|
} else {
|
|
545
543
|
const bytes = r.readBytes(length);
|
|
@@ -1028,9 +1028,9 @@ function encodeAuthorizationToken(token, w) {
|
|
|
1028
1028
|
}
|
|
1029
1029
|
if (aliasType === 1 || aliasType === 3) {
|
|
1030
1030
|
w.writeVarInt(_nullishCoalesce(token.token_type, () => ( 0n)));
|
|
1031
|
-
const
|
|
1032
|
-
w.writeVarInt(BigInt(
|
|
1033
|
-
w.writeBytes(
|
|
1031
|
+
const tv = _nullishCoalesce(token.token_value, () => ( new Uint8Array(0)));
|
|
1032
|
+
w.writeVarInt(BigInt(tv.byteLength));
|
|
1033
|
+
w.writeBytes(tv);
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
1036
1036
|
function decodeAuthorizationToken(r) {
|
|
@@ -1046,7 +1046,7 @@ function decodeAuthorizationToken(r) {
|
|
|
1046
1046
|
result.token_type = r.readVarInt();
|
|
1047
1047
|
const len = Number(r.readVarInt());
|
|
1048
1048
|
const bytes = r.readBytes(len);
|
|
1049
|
-
result.token_value =
|
|
1049
|
+
result.token_value = bytes;
|
|
1050
1050
|
}
|
|
1051
1051
|
return result;
|
|
1052
1052
|
}
|
|
@@ -1261,7 +1261,7 @@ function encodeTrackProperties(props, writer) {
|
|
|
1261
1261
|
entries.push({
|
|
1262
1262
|
type: TPROP_IMMUTABLE_PROPERTIES,
|
|
1263
1263
|
encode: (w) => {
|
|
1264
|
-
const raw =
|
|
1264
|
+
const raw = props.immutable_properties;
|
|
1265
1265
|
w.writeVarInt(BigInt(raw.byteLength));
|
|
1266
1266
|
w.writeBytes(raw);
|
|
1267
1267
|
}
|
|
@@ -1346,7 +1346,7 @@ function decodeTrackProperties(reader, payloadEnd) {
|
|
|
1346
1346
|
const length = Number(reader.readVarInt());
|
|
1347
1347
|
const bytes = reader.readBytes(length);
|
|
1348
1348
|
if (propType === TPROP_IMMUTABLE_PROPERTIES) {
|
|
1349
|
-
result.immutable_properties =
|
|
1349
|
+
result.immutable_properties = bytes;
|
|
1350
1350
|
} else {
|
|
1351
1351
|
unknown.push({
|
|
1352
1352
|
id: `0x${propType.toString(16)}`,
|
|
@@ -830,7 +830,7 @@ function encodeSetupParams(params, writer) {
|
|
|
830
830
|
tmpWriter.writeVarInt(tok.token_type);
|
|
831
831
|
}
|
|
832
832
|
if (tok.token_value !== void 0) {
|
|
833
|
-
tmpWriter.writeBytes(
|
|
833
|
+
tmpWriter.writeBytes(tok.token_value);
|
|
834
834
|
}
|
|
835
835
|
if (tok.token_alias !== void 0) {
|
|
836
836
|
tmpWriter.writeVarInt(tok.token_alias);
|
|
@@ -902,7 +902,7 @@ function decodeSetupParams(reader) {
|
|
|
902
902
|
tok.token_type = tokenReader.readVarInt();
|
|
903
903
|
if (tokenReader.remaining > 0) {
|
|
904
904
|
const tokenValue = tokenReader.readBytesView(tokenReader.remaining);
|
|
905
|
-
tok.token_value =
|
|
905
|
+
tok.token_value = new Uint8Array(tokenValue);
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
result.authorization_token = tok;
|
|
@@ -942,7 +942,7 @@ function encodeMessageParams(params, writer) {
|
|
|
942
942
|
tmpWriter.writeVarInt(tok.token_type);
|
|
943
943
|
}
|
|
944
944
|
if (tok.token_value !== void 0) {
|
|
945
|
-
tmpWriter.writeBytes(
|
|
945
|
+
tmpWriter.writeBytes(tok.token_value);
|
|
946
946
|
}
|
|
947
947
|
if (tok.token_alias !== void 0) {
|
|
948
948
|
tmpWriter.writeVarInt(tok.token_alias);
|
|
@@ -998,7 +998,7 @@ function decodeMessageParams(reader) {
|
|
|
998
998
|
tok.token_type = tokenReader.readVarInt();
|
|
999
999
|
if (tokenReader.remaining > 0) {
|
|
1000
1000
|
const tokenValue = tokenReader.readBytesView(tokenReader.remaining);
|
|
1001
|
-
tok.token_value =
|
|
1001
|
+
tok.token_value = new Uint8Array(tokenValue);
|
|
1002
1002
|
}
|
|
1003
1003
|
}
|
|
1004
1004
|
result.authorization_token = tok;
|
|
@@ -852,8 +852,7 @@ function encodeAuthorizationToken(token, w) {
|
|
|
852
852
|
}
|
|
853
853
|
if (aliasType === 1 || aliasType === 3) {
|
|
854
854
|
w.writeVarInt(token.token_type);
|
|
855
|
-
|
|
856
|
-
w.writeBytes(tokenBytes);
|
|
855
|
+
w.writeBytes(token.token_value);
|
|
857
856
|
}
|
|
858
857
|
}
|
|
859
858
|
function decodeAuthorizationToken(r) {
|
|
@@ -865,8 +864,7 @@ function decodeAuthorizationToken(r) {
|
|
|
865
864
|
}
|
|
866
865
|
if (aliasType === 1 || aliasType === 3) {
|
|
867
866
|
result.token_type = r.readVarInt();
|
|
868
|
-
|
|
869
|
-
result.token_value_hex = bytesToHex(tokenBytes);
|
|
867
|
+
result.token_value = r.readBytes(r.remaining);
|
|
870
868
|
}
|
|
871
869
|
return result;
|
|
872
870
|
}
|