@moqtap/codec 0.10.0 → 0.12.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/CHANGELOG.md +175 -0
- package/README.md +3 -2
- package/dist/auth-redaction-B9JJOlIY.d.cts +100 -0
- package/dist/auth-redaction-B9JJOlIY.d.ts +100 -0
- package/dist/{chunk-RYKQXBG6.cjs → chunk-27K5BUAG.cjs} +481 -517
- package/dist/{chunk-WCPENYNE.js → chunk-3RNQMGKA.js} +359 -390
- package/dist/{chunk-TSUZ6GJV.js → chunk-5AWIXOLB.js} +438 -469
- package/dist/{chunk-4XLYCT5Q.cjs → chunk-5IMHJIAZ.cjs} +52 -2
- package/dist/{chunk-KTNERCTZ.cjs → chunk-5JGZCKJO.cjs} +488 -518
- package/dist/{chunk-WCTMPDUX.cjs → chunk-73K4OSAN.cjs} +401 -432
- package/dist/{chunk-N2V7Z37J.js → chunk-7VQKRGRD.js} +19 -3
- package/dist/{chunk-BIBMDTU5.cjs → chunk-A3UCQOIB.cjs} +486 -517
- package/dist/{chunk-K2B3OVFK.js → chunk-A7PDMVXM.js} +24 -3
- package/dist/{chunk-VZHL2U2N.cjs → chunk-A7VQO6YC.cjs} +54 -38
- package/dist/{chunk-AQ445VV7.cjs → chunk-ACQHZLRZ.cjs} +66 -46
- package/dist/{chunk-L5DRACXS.js → chunk-BBICOMW6.js} +25 -3
- package/dist/{chunk-T4VWPKDG.cjs → chunk-BNIUVOCJ.cjs} +529 -599
- package/dist/{chunk-UKKDOOUO.cjs → chunk-CNC6ANS4.cjs} +58 -37
- package/dist/{chunk-OZRK73NQ.js → chunk-FDLQSWHK.js} +433 -469
- package/dist/{chunk-IJAF6PAY.js → chunk-FFNOE4UR.js} +19 -3
- package/dist/{chunk-CZNW3R5E.cjs → chunk-GMENT63T.cjs} +54 -38
- package/dist/{chunk-XCRCAUGI.cjs → chunk-HM7BLRVP.cjs} +291 -297
- package/dist/{chunk-XHAP2YST.js → chunk-HZONHFNO.js} +19 -3
- package/dist/{chunk-SDJTSMOW.cjs → chunk-ILYABWDK.cjs} +60 -38
- package/dist/{chunk-IDV2JYJ6.cjs → chunk-KRSFTZVO.cjs} +56 -40
- package/dist/{chunk-RIIA4ALQ.cjs → chunk-KSPH6KMH.cjs} +54 -38
- package/dist/{chunk-OUNN4IQB.cjs → chunk-KTY4LAUV.cjs} +482 -497
- package/dist/chunk-OBATJBNL.cjs +868 -0
- package/dist/{chunk-RZTLQAKF.js → chunk-OJNLJT5D.js} +0 -1
- package/dist/{chunk-FT3HDNIB.js → chunk-PJK2ZS3I.js} +467 -537
- package/dist/{chunk-G53WYUXL.js → chunk-PSMZKHOC.js} +441 -456
- package/dist/{chunk-7RE6KSIY.cjs → chunk-Q5JQGHWY.cjs} +0 -1
- package/dist/{chunk-BX2N7TZM.js → chunk-SQTUQY5E.js} +31 -11
- package/dist/chunk-T2TCLZZV.js +2304 -0
- package/dist/chunk-WB65UG7G.cjs +2304 -0
- package/dist/{chunk-CF5DDD3Q.js → chunk-WCRLAKDW.js} +239 -245
- package/dist/{chunk-KYVTM5VI.js → chunk-WL2YUEXH.js} +19 -3
- package/dist/chunk-XPCBPACE.js +868 -0
- package/dist/{chunk-KGDVC4O6.js → chunk-YCNIVZS7.js} +439 -469
- package/dist/{chunk-KLPWJU6W.js → chunk-YRZQVNZO.js} +50 -0
- package/dist/{codec-DOmAmFkI.d.ts → codec-B-oUU-cp.d.ts} +21 -2
- package/dist/{codec-DNmiG2e0.d.cts → codec-B3RpCI9k.d.cts} +21 -2
- package/dist/codec-B5pcOS3m.d.cts +38 -0
- package/dist/{codec-BNy5syCU.d.ts → codec-BLjxwK2f.d.ts} +21 -2
- package/dist/{codec-alVA27Uc.d.ts → codec-BV3CL_3m.d.ts} +21 -2
- package/dist/{codec-Czejcmxr.d.cts → codec-BYCDWgED.d.ts} +22 -3
- package/dist/{codec-DaXjRrOV.d.ts → codec-Bwqq7ZQT.d.ts} +22 -3
- package/dist/{codec-CFWu5KVT.d.cts → codec-BzWxOFby.d.cts} +22 -3
- package/dist/codec-C-nJhNNl.d.ts +38 -0
- package/dist/{codec-EvybrBuY.d.cts → codec-C11VEPbQ.d.cts} +21 -2
- package/dist/codec-C9U0Vzy9.d.cts +68 -0
- package/dist/{codec-Cm3D261p.d.ts → codec-CCx22jnd.d.ts} +21 -2
- package/dist/{codec-CwZ6OCPP.d.ts → codec-CP3zkfTv.d.ts} +22 -3
- package/dist/{codec-BxQTybUX.d.cts → codec-CRmOLDpt.d.ts} +22 -3
- package/dist/{codec-DzkdW4kO.d.cts → codec-CUu56DDU.d.cts} +21 -2
- package/dist/codec-Cb8mUre3.d.ts +68 -0
- package/dist/{codec-f7vxeVIS.d.cts → codec-CemqAQdR.d.cts} +22 -3
- package/dist/{codec-gl4_QEva.d.ts → codec-CxMvsVMS.d.ts} +21 -2
- package/dist/{codec-Bd60SGKH.d.cts → codec-DIgMVTi4.d.cts} +21 -2
- package/dist/{codec--CVeqGQv.d.ts → codec-DYzozF68.d.ts} +21 -2
- package/dist/{codec-CWnf8mn_.d.cts → codec-D_O9zxax.d.ts} +22 -3
- package/dist/{codec-08ZOr5wI.d.ts → codec-Ddc-QxV6.d.cts} +22 -3
- package/dist/{codec-CBdOIfMY.d.ts → codec-DoojMl2z.d.cts} +22 -3
- package/dist/{codec-BrTi4K7r.d.cts → codec-DqdjiOQF.d.ts} +22 -3
- package/dist/{codec-CXCjya_G.d.cts → codec-Dstemwrq.d.cts} +21 -2
- package/dist/{codec-Dl_QIXMz.d.ts → codec-Dt6nuejp.d.ts} +21 -2
- package/dist/{codec-Bux_Q40X.d.ts → codec-DuYhd8fP.d.cts} +22 -3
- package/dist/{codec-Cfo_z5ET.d.cts → codec-DwNOFgPU.d.cts} +21 -2
- package/dist/{codec-guzqtfMk.d.cts → codec-l01BpNhF.d.cts} +21 -2
- package/dist/{codec-C3mdsGx3.d.ts → codec-psN2vB6I.d.cts} +22 -3
- package/dist/draft07-session.d.cts +3 -3
- package/dist/draft07-session.d.ts +3 -3
- package/dist/draft07.cjs +48 -4
- package/dist/draft07.d.cts +44 -4
- package/dist/draft07.d.ts +44 -4
- package/dist/draft07.js +49 -5
- package/dist/draft08-session.d.cts +3 -3
- package/dist/draft08-session.d.ts +3 -3
- package/dist/draft08.cjs +76 -3
- package/dist/draft08.d.cts +73 -5
- package/dist/draft08.d.ts +73 -5
- package/dist/draft08.js +77 -4
- package/dist/draft09-session.d.cts +3 -3
- package/dist/draft09-session.d.ts +3 -3
- package/dist/draft09.cjs +76 -3
- package/dist/draft09.d.cts +73 -5
- package/dist/draft09.d.ts +73 -5
- package/dist/draft09.js +77 -4
- package/dist/draft10-session.d.cts +3 -3
- package/dist/draft10-session.d.ts +3 -3
- package/dist/draft10.cjs +76 -3
- package/dist/draft10.d.cts +73 -5
- package/dist/draft10.d.ts +73 -5
- package/dist/draft10.js +77 -4
- package/dist/draft11-session.d.cts +3 -3
- package/dist/draft11-session.d.ts +3 -3
- package/dist/draft11.cjs +104 -3
- package/dist/draft11.d.cts +101 -5
- package/dist/draft11.d.ts +101 -5
- package/dist/draft11.js +105 -4
- package/dist/draft12-session.d.cts +3 -3
- package/dist/draft12-session.d.ts +3 -3
- package/dist/draft12.cjs +113 -3
- package/dist/draft12.d.cts +110 -5
- package/dist/draft12.d.ts +110 -5
- package/dist/draft12.js +114 -4
- package/dist/draft13-session.d.cts +3 -3
- package/dist/draft13-session.d.ts +3 -3
- package/dist/draft13.cjs +105 -3
- package/dist/draft13.d.cts +102 -5
- package/dist/draft13.d.ts +102 -5
- package/dist/draft13.js +106 -4
- package/dist/draft14-session.d.cts +3 -3
- package/dist/draft14-session.d.ts +3 -3
- package/dist/draft14.cjs +107 -3
- package/dist/draft14.d.cts +104 -31
- package/dist/draft14.d.ts +104 -31
- package/dist/draft14.js +108 -4
- package/dist/draft15-session.d.cts +4 -4
- package/dist/draft15-session.d.ts +4 -4
- package/dist/draft15.cjs +69 -3
- package/dist/draft15.d.cts +68 -25
- package/dist/draft15.d.ts +68 -25
- package/dist/draft15.js +70 -4
- package/dist/draft16-session.cjs +2 -2
- package/dist/draft16-session.d.cts +4 -4
- package/dist/draft16-session.d.ts +4 -4
- package/dist/draft16-session.js +1 -1
- package/dist/draft16.cjs +76 -8
- package/dist/draft16.d.cts +70 -16
- package/dist/draft16.d.ts +70 -16
- package/dist/draft16.js +73 -5
- package/dist/draft17-session.d.cts +4 -4
- package/dist/draft17-session.d.ts +4 -4
- package/dist/draft17.cjs +77 -3
- package/dist/draft17.d.cts +75 -6
- package/dist/draft17.d.ts +75 -6
- package/dist/draft17.js +78 -4
- package/dist/draft18-session.d.cts +4 -4
- package/dist/draft18-session.d.ts +4 -4
- package/dist/draft18.cjs +8 -3
- package/dist/draft18.d.cts +6 -5
- package/dist/draft18.d.ts +6 -5
- package/dist/draft18.js +9 -4
- package/dist/draft19-session.d.cts +4 -4
- package/dist/draft19-session.d.ts +4 -4
- package/dist/draft19.cjs +11 -4
- package/dist/draft19.d.cts +6 -5
- package/dist/draft19.d.ts +6 -5
- package/dist/draft19.js +12 -5
- package/dist/draft20-session.d.cts +4 -4
- package/dist/draft20-session.d.ts +4 -4
- package/dist/draft20.cjs +8 -3
- package/dist/draft20.d.cts +6 -5
- package/dist/draft20.d.ts +6 -5
- package/dist/draft20.js +9 -4
- package/dist/draft21-session.cjs +8 -0
- package/dist/draft21-session.d.cts +195 -0
- package/dist/draft21-session.d.ts +195 -0
- package/dist/draft21-session.js +8 -0
- package/dist/draft21.cjs +222 -0
- package/dist/draft21.d.cts +176 -0
- package/dist/draft21.d.ts +176 -0
- package/dist/draft21.js +222 -0
- package/dist/index.cjs +37 -31
- package/dist/index.d.cts +29 -23
- package/dist/index.d.ts +29 -23
- package/dist/index.js +22 -16
- package/dist/{session-types-Dn7b3RZM.d.ts → session-types-Cxx7W8zZ.d.ts} +1 -1
- package/dist/{session-types-B9idsih-.d.cts → session-types-DZkI_qFG.d.cts} +1 -1
- package/dist/session.cjs +7 -3
- package/dist/session.d.cts +11 -9
- package/dist/session.d.ts +11 -9
- package/dist/session.js +6 -2
- package/dist/{types-DG5_iKuv.d.cts → types-2NisMA8_.d.cts} +1 -1
- package/dist/{types-DG5_iKuv.d.ts → types-2NisMA8_.d.ts} +1 -1
- package/dist/{types-C1rxh-id.d.cts → types-Btp416jt.d.cts} +8 -0
- package/dist/{types-C1rxh-id.d.ts → types-Btp416jt.d.ts} +8 -0
- package/dist/{types-CZTzbIzQ.d.cts → types-BxTL1NXw.d.cts} +7 -0
- package/dist/{types-CZTzbIzQ.d.ts → types-BxTL1NXw.d.ts} +7 -0
- package/dist/{types-pShrs3fN.d.cts → types-CuibHYcf.d.cts} +7 -0
- package/dist/{types-pShrs3fN.d.ts → types-CuibHYcf.d.ts} +7 -0
- package/dist/{types-CY-QWxd-.d.cts → types-CwN7AEoL.d.cts} +13 -1
- package/dist/{types-CY-QWxd-.d.ts → types-CwN7AEoL.d.ts} +13 -1
- package/dist/{types-D4bmW8rY.d.cts → types-DAnalt5K.d.cts} +7 -0
- package/dist/{types-D4bmW8rY.d.ts → types-DAnalt5K.d.ts} +7 -0
- package/dist/types-DDwidj2d.d.cts +371 -0
- package/dist/types-DDwidj2d.d.ts +371 -0
- package/dist/{types-Bg86GGVZ.d.cts → types-DxXNP32l.d.cts} +8 -0
- package/dist/{types-Bg86GGVZ.d.ts → types-DxXNP32l.d.ts} +8 -0
- package/package.json +15 -3
- package/src/core/auth-redaction.ts +190 -0
- package/src/core/types.ts +1 -0
- package/src/drafts/draft07/codec.ts +40 -0
- package/src/drafts/draft07/error-codes.ts +44 -0
- package/src/drafts/draft07/index.ts +15 -0
- package/src/drafts/draft08/codec.ts +40 -0
- package/src/drafts/draft08/error-codes.ts +76 -0
- package/src/drafts/draft08/index.ts +21 -0
- package/src/drafts/draft09/codec.ts +40 -0
- package/src/drafts/draft09/error-codes.ts +76 -0
- package/src/drafts/draft09/index.ts +21 -0
- package/src/drafts/draft10/codec.ts +40 -0
- package/src/drafts/draft10/error-codes.ts +76 -0
- package/src/drafts/draft10/index.ts +21 -0
- package/src/drafts/draft11/codec.ts +60 -0
- package/src/drafts/draft11/error-codes.ts +106 -0
- package/src/drafts/draft11/index.ts +23 -0
- package/src/drafts/draft12/codec.ts +61 -0
- package/src/drafts/draft12/error-codes.ts +116 -0
- package/src/drafts/draft12/index.ts +25 -0
- package/src/drafts/draft13/codec.ts +69 -11
- package/src/drafts/draft13/error-codes.ts +107 -0
- package/src/drafts/draft13/index.ts +23 -0
- package/src/drafts/draft13/types.ts +1 -1
- package/src/drafts/draft14/codec.ts +65 -4
- package/src/drafts/draft14/data-streams.ts +331 -259
- package/src/drafts/draft14/error-codes.ts +109 -0
- package/src/drafts/draft14/index.ts +23 -0
- package/src/drafts/draft15/codec.ts +65 -8
- package/src/drafts/draft15/data-streams.ts +489 -444
- package/src/drafts/draft15/error-codes.ts +66 -0
- package/src/drafts/draft15/index.ts +15 -0
- package/src/drafts/draft15/types.ts +7 -0
- package/src/drafts/draft16/codec.ts +146 -52
- package/src/drafts/draft16/data-streams.ts +513 -442
- package/src/drafts/draft16/error-codes.ts +68 -0
- package/src/drafts/draft16/index.ts +15 -0
- package/src/drafts/draft16/messages.ts +4 -4
- package/src/drafts/draft16/session-fsm.ts +0 -1
- package/src/drafts/draft16/types.ts +22 -8
- package/src/drafts/draft17/codec.ts +73 -12
- package/src/drafts/draft17/data-streams.ts +567 -488
- package/src/drafts/draft17/index.ts +15 -0
- package/src/drafts/draft17/types.ts +7 -0
- package/src/drafts/draft18/codec.ts +66 -9
- package/src/drafts/draft18/data-streams.ts +570 -482
- package/src/drafts/draft18/index.ts +3 -0
- package/src/drafts/draft18/types.ts +16 -8
- package/src/drafts/draft19/codec.ts +70 -10
- package/src/drafts/draft19/data-streams.ts +570 -482
- package/src/drafts/draft19/error-codes.ts +3 -1
- package/src/drafts/draft19/index.ts +3 -0
- package/src/drafts/draft19/messages.ts +3 -1
- package/src/drafts/draft19/types.ts +22 -11
- package/src/drafts/draft20/codec.ts +90 -23
- package/src/drafts/draft20/data-streams.ts +610 -563
- package/src/drafts/draft20/index.ts +3 -0
- package/src/drafts/draft20/types.ts +14 -2
- package/src/drafts/draft21/codec.ts +1816 -0
- package/src/drafts/draft21/data-streams.ts +1338 -0
- package/src/drafts/draft21/error-codes.ts +135 -0
- package/src/drafts/draft21/index.ts +168 -0
- package/src/drafts/draft21/messages.ts +68 -0
- package/src/drafts/draft21/rules.ts +107 -0
- package/src/drafts/draft21/session-fsm.ts +1063 -0
- package/src/drafts/draft21/session.ts +21 -0
- package/src/drafts/draft21/types.ts +473 -0
- package/src/index.ts +9 -1
- package/src/session.ts +4 -0
- package/dist/codec-BJ7BHuiZ.d.ts +0 -18
- package/dist/codec-C6Mx4bwz.d.cts +0 -18
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
interface UnknownParam {
|
|
2
|
+
readonly id: string;
|
|
3
|
+
readonly length: number;
|
|
4
|
+
readonly raw_hex: string;
|
|
5
|
+
}
|
|
6
|
+
interface AuthorizationToken {
|
|
7
|
+
readonly alias_type: bigint;
|
|
8
|
+
readonly token_alias?: bigint;
|
|
9
|
+
readonly token_type?: bigint;
|
|
10
|
+
readonly token_value?: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
interface Draft21SetupOptions {
|
|
13
|
+
path?: string;
|
|
14
|
+
authorization_token?: readonly AuthorizationToken[];
|
|
15
|
+
max_auth_token_cache_size?: bigint;
|
|
16
|
+
authority?: string;
|
|
17
|
+
max_filter_ranges?: bigint;
|
|
18
|
+
moqt_implementation?: string;
|
|
19
|
+
max_request_updates?: bigint;
|
|
20
|
+
unknown?: UnknownParam[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* LOCATION_FILTER (0x21) — draft-21 Section 3.3.1.
|
|
24
|
+
*
|
|
25
|
+
* The draft-19 `Filter Type` discriminator (0x1 Next Group Start / 0x2 Largest
|
|
26
|
+
* Object / 0x3 AbsoluteStart / 0x4 AbsoluteRange) is gone; the shape is the
|
|
27
|
+
* number of vi64 fields the value holds. The fields below carry the wire
|
|
28
|
+
* values verbatim, unresolved:
|
|
29
|
+
*
|
|
30
|
+
* - 1 field → `start_group` alone is RELATIVE: start is
|
|
31
|
+
* {Largest Object.Group + 1 - start_group, 0}. Open-ended.
|
|
32
|
+
* - 2 fields → absolute start {start_group, start_object}, except that
|
|
33
|
+
* {0, 0} means the Next Object. Open-ended.
|
|
34
|
+
* - 3 fields → end group is `start_group + end_group_delta`; `end_object`
|
|
35
|
+
* omitted means every object in that group.
|
|
36
|
+
* - 4 fields → the fully specified INCLUSIVE range
|
|
37
|
+
* {start_group, start_object} … {start_group + end_group_delta,
|
|
38
|
+
* end_object}.
|
|
39
|
+
*
|
|
40
|
+
* `removed: true` is the `Length == 0` form, which in REQUEST_UPDATE removes an
|
|
41
|
+
* existing filter.
|
|
42
|
+
*
|
|
43
|
+
* The range is inclusive at both ends. draft-19's "last Object, plus 1" and
|
|
44
|
+
* "Object == 0 means the whole group" conventions are gone, so nothing here is
|
|
45
|
+
* ever incremented or decremented on the way in or out.
|
|
46
|
+
*/
|
|
47
|
+
interface LocationFilter {
|
|
48
|
+
readonly start_group?: bigint;
|
|
49
|
+
readonly start_object?: bigint;
|
|
50
|
+
readonly end_group_delta?: bigint;
|
|
51
|
+
readonly end_object?: bigint;
|
|
52
|
+
readonly removed?: boolean;
|
|
53
|
+
}
|
|
54
|
+
interface RangeFilterRange {
|
|
55
|
+
readonly start: bigint;
|
|
56
|
+
readonly end?: bigint;
|
|
57
|
+
}
|
|
58
|
+
interface RangeFilter {
|
|
59
|
+
readonly set_id?: bigint;
|
|
60
|
+
readonly property_type?: bigint;
|
|
61
|
+
readonly ranges?: RangeFilterRange[];
|
|
62
|
+
readonly removed?: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface LargestObject {
|
|
65
|
+
readonly group: bigint;
|
|
66
|
+
readonly object: bigint;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* FILL_PARAMETERS (0x23) value — NEW in draft-20 (Section 9.20.16).
|
|
70
|
+
*
|
|
71
|
+
* A separate parameter scope carrying only the settings that differ from the
|
|
72
|
+
* subscription's. Table 6 closes the list to exactly these eight types;
|
|
73
|
+
* TRACK_PROPERTY_FILTER (0x29) is deliberately excluded.
|
|
74
|
+
*
|
|
75
|
+
* The mere presence of the parameter is what requests a fill fetch stream, so
|
|
76
|
+
* an empty value is meaningful — it asks for a fill with everything inherited.
|
|
77
|
+
*/
|
|
78
|
+
interface Draft21FillParameters {
|
|
79
|
+
fill_timeout?: bigint;
|
|
80
|
+
subscriber_priority?: bigint;
|
|
81
|
+
location_filter?: LocationFilter;
|
|
82
|
+
group_order?: bigint;
|
|
83
|
+
subgroup_filter?: readonly RangeFilter[];
|
|
84
|
+
objectid_filter?: readonly RangeFilter[];
|
|
85
|
+
priority_filter?: readonly RangeFilter[];
|
|
86
|
+
object_property_filter?: readonly RangeFilter[];
|
|
87
|
+
}
|
|
88
|
+
interface Draft21Params {
|
|
89
|
+
object_delivery_timeout?: bigint;
|
|
90
|
+
authorization_token?: readonly AuthorizationToken[];
|
|
91
|
+
rendezvous_timeout?: bigint;
|
|
92
|
+
subgroup_delivery_timeout?: bigint;
|
|
93
|
+
expires?: bigint;
|
|
94
|
+
largest_object?: LargestObject;
|
|
95
|
+
fill_timeout?: bigint;
|
|
96
|
+
forward?: bigint;
|
|
97
|
+
subscriber_priority?: bigint;
|
|
98
|
+
location_filter?: LocationFilter;
|
|
99
|
+
group_order?: bigint;
|
|
100
|
+
fill_parameters?: Draft21FillParameters;
|
|
101
|
+
subgroup_filter?: readonly RangeFilter[];
|
|
102
|
+
objectid_filter?: readonly RangeFilter[];
|
|
103
|
+
priority_filter?: readonly RangeFilter[];
|
|
104
|
+
object_property_filter?: readonly RangeFilter[];
|
|
105
|
+
track_property_filter?: readonly RangeFilter[];
|
|
106
|
+
new_group_request?: bigint;
|
|
107
|
+
track_namespace_prefix?: string[];
|
|
108
|
+
include_properties?: bigint;
|
|
109
|
+
unknown?: UnknownParam[];
|
|
110
|
+
}
|
|
111
|
+
interface Draft21TrackProperties {
|
|
112
|
+
object_delivery_timeout?: bigint;
|
|
113
|
+
max_cache_duration?: bigint;
|
|
114
|
+
subgroup_delivery_timeout?: bigint;
|
|
115
|
+
immutable_properties?: Uint8Array;
|
|
116
|
+
default_publisher_priority?: bigint;
|
|
117
|
+
default_publisher_group_order?: bigint;
|
|
118
|
+
dynamic_groups?: bigint;
|
|
119
|
+
unknown?: UnknownParam[];
|
|
120
|
+
}
|
|
121
|
+
type Draft21MessageType = 'setup' | 'subscribe' | 'subscribe_ok' | 'request_update' | 'publish_state_notify' | 'publish' | 'publish_done' | 'publish_namespace' | 'namespace' | 'namespace_done' | 'subscribe_namespace' | 'subscribe_tracks' | 'publish_skipped' | 'fetch' | 'fetch_ok' | 'track_status' | 'request_ok' | 'request_error' | 'goaway';
|
|
122
|
+
interface Draft21BaseMessage {
|
|
123
|
+
readonly type: Draft21MessageType;
|
|
124
|
+
}
|
|
125
|
+
interface Draft21Setup extends Draft21BaseMessage {
|
|
126
|
+
readonly type: 'setup';
|
|
127
|
+
readonly options: Draft21SetupOptions;
|
|
128
|
+
}
|
|
129
|
+
interface Draft21Subscribe extends Draft21BaseMessage {
|
|
130
|
+
readonly type: 'subscribe';
|
|
131
|
+
readonly request_id: bigint;
|
|
132
|
+
readonly track_namespace: string[];
|
|
133
|
+
readonly track_name: string;
|
|
134
|
+
readonly parameters: Draft21Params;
|
|
135
|
+
}
|
|
136
|
+
interface Draft21SubscribeOk extends Draft21BaseMessage {
|
|
137
|
+
readonly type: 'subscribe_ok';
|
|
138
|
+
readonly track_alias: bigint;
|
|
139
|
+
readonly parameters: Draft21Params;
|
|
140
|
+
readonly track_properties: Draft21TrackProperties;
|
|
141
|
+
}
|
|
142
|
+
interface Draft21RequestUpdate extends Draft21BaseMessage {
|
|
143
|
+
readonly type: 'request_update';
|
|
144
|
+
readonly request_id: bigint;
|
|
145
|
+
readonly parameters: Draft21Params;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* PUBLISH_STATE_NOTIFY (0x22) — NEW in draft-20, Section 9.10.
|
|
149
|
+
*
|
|
150
|
+
* No Request ID: the message is identified by the subscription's bidirectional
|
|
151
|
+
* stream it arrives on, like SUBSCRIBE_OK, PUBLISH_DONE and FETCH_OK.
|
|
152
|
+
*/
|
|
153
|
+
interface Draft21PublishStateNotify extends Draft21BaseMessage {
|
|
154
|
+
readonly type: 'publish_state_notify';
|
|
155
|
+
readonly parameters: Draft21Params;
|
|
156
|
+
}
|
|
157
|
+
interface Draft21Publish extends Draft21BaseMessage {
|
|
158
|
+
readonly type: 'publish';
|
|
159
|
+
readonly request_id: bigint;
|
|
160
|
+
readonly track_namespace: string[];
|
|
161
|
+
readonly track_name: string;
|
|
162
|
+
readonly track_alias: bigint;
|
|
163
|
+
readonly parameters: Draft21Params;
|
|
164
|
+
readonly track_properties: Draft21TrackProperties;
|
|
165
|
+
}
|
|
166
|
+
interface Draft21PublishDone extends Draft21BaseMessage {
|
|
167
|
+
readonly type: 'publish_done';
|
|
168
|
+
readonly status_code: bigint;
|
|
169
|
+
/**
|
|
170
|
+
* Number of streams opened for the subscription, including empty subgroups
|
|
171
|
+
* and — new in draft-20 — fill fetch streams (Section 9.9), or the
|
|
172
|
+
* `UNKNOWN_STREAM_COUNT` sentinel exported from `codec.ts` (2^64 - 1, raised
|
|
173
|
+
* from draft-19's 2^62 - 1) when the publisher cannot state it exactly.
|
|
174
|
+
*
|
|
175
|
+
* `bigint`, not `number`: the sentinel does not fit a double.
|
|
176
|
+
*/
|
|
177
|
+
readonly stream_count: bigint;
|
|
178
|
+
readonly reason_phrase: string;
|
|
179
|
+
}
|
|
180
|
+
interface Draft21PublishNamespace extends Draft21BaseMessage {
|
|
181
|
+
readonly type: 'publish_namespace';
|
|
182
|
+
readonly request_id: bigint;
|
|
183
|
+
readonly track_namespace: string[];
|
|
184
|
+
readonly parameters: Draft21Params;
|
|
185
|
+
}
|
|
186
|
+
interface Draft21Namespace extends Draft21BaseMessage {
|
|
187
|
+
readonly type: 'namespace';
|
|
188
|
+
readonly namespace_suffix: string[];
|
|
189
|
+
}
|
|
190
|
+
interface Draft21NamespaceDone extends Draft21BaseMessage {
|
|
191
|
+
readonly type: 'namespace_done';
|
|
192
|
+
readonly namespace_suffix: string[];
|
|
193
|
+
}
|
|
194
|
+
interface Draft21SubscribeNamespace extends Draft21BaseMessage {
|
|
195
|
+
readonly type: 'subscribe_namespace';
|
|
196
|
+
readonly request_id: bigint;
|
|
197
|
+
readonly namespace_prefix: string[];
|
|
198
|
+
readonly parameters: Draft21Params;
|
|
199
|
+
}
|
|
200
|
+
interface Draft21SubscribeTracks extends Draft21BaseMessage {
|
|
201
|
+
readonly type: 'subscribe_tracks';
|
|
202
|
+
readonly request_id: bigint;
|
|
203
|
+
readonly namespace_prefix: string[];
|
|
204
|
+
readonly parameters: Draft21Params;
|
|
205
|
+
}
|
|
206
|
+
interface Draft21PublishSkipped extends Draft21BaseMessage {
|
|
207
|
+
readonly type: 'publish_skipped';
|
|
208
|
+
readonly namespace_suffix: string[];
|
|
209
|
+
readonly track_name: string;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* FETCH (0x16) — the byte layout was rewritten in draft-20 (Section 9.11).
|
|
213
|
+
*
|
|
214
|
+
* Gone: the `Fetch Type` field and its enum, the `Standalone Fetch` and
|
|
215
|
+
* `Joining Fetch` structures, and the inline `Start Location` / `End Location`.
|
|
216
|
+
* Track Namespace and Track Name are inline fields of FETCH itself, and the
|
|
217
|
+
* range travels in the LOCATION_FILTER parameter. A FETCH with no
|
|
218
|
+
* LOCATION_FILTER covers {0,0} through Largest Object, inclusive.
|
|
219
|
+
*
|
|
220
|
+
* The codepoint did not change, so a draft-19 decoder fed one of these reads
|
|
221
|
+
* the Number of Track Namespace Fields count as a Fetch Type and mis-parses in
|
|
222
|
+
* silence. There is no in-band version signal that catches it.
|
|
223
|
+
*/
|
|
224
|
+
interface Draft21Fetch extends Draft21BaseMessage {
|
|
225
|
+
readonly type: 'fetch';
|
|
226
|
+
readonly request_id: bigint;
|
|
227
|
+
readonly track_namespace: string[];
|
|
228
|
+
readonly track_name: string;
|
|
229
|
+
readonly parameters: Draft21Params;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* FETCH_OK (0x18) — bytes identical to draft-19, meaning changed.
|
|
233
|
+
*
|
|
234
|
+
* `end_group` / `end_object` are the INCLUSIVE end of the range the response
|
|
235
|
+
* covers: the last Object actually covered. draft-19's "the last Object, plus
|
|
236
|
+
* 1; or 0 to indicate the entire Group" is gone (draft-21 Section 9.12 with
|
|
237
|
+
* Section 3.3.1). The same bytes mean a different object under the two drafts,
|
|
238
|
+
* and nothing on the wire distinguishes them.
|
|
239
|
+
*/
|
|
240
|
+
interface Draft21FetchOk extends Draft21BaseMessage {
|
|
241
|
+
readonly type: 'fetch_ok';
|
|
242
|
+
readonly end_of_track: number;
|
|
243
|
+
readonly end_group: bigint;
|
|
244
|
+
readonly end_object: bigint;
|
|
245
|
+
readonly parameters: Draft21Params;
|
|
246
|
+
readonly track_properties: Draft21TrackProperties;
|
|
247
|
+
}
|
|
248
|
+
interface Draft21TrackStatus extends Draft21BaseMessage {
|
|
249
|
+
readonly type: 'track_status';
|
|
250
|
+
readonly request_id: bigint;
|
|
251
|
+
readonly track_namespace: string[];
|
|
252
|
+
readonly track_name: string;
|
|
253
|
+
readonly parameters: Draft21Params;
|
|
254
|
+
}
|
|
255
|
+
interface Draft21RequestOk extends Draft21BaseMessage {
|
|
256
|
+
readonly type: 'request_ok';
|
|
257
|
+
readonly parameters: Draft21Params;
|
|
258
|
+
readonly track_properties: Draft21TrackProperties;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Redirect structure, used inside REQUEST_ERROR when error_code = REDIRECT (0x34).
|
|
262
|
+
*
|
|
263
|
+
* draft-21 Section 9.4.1 names the namespace/name pair "the Redirect target"
|
|
264
|
+
* and dropped draft-19's rule that a zero-length pair meant "reuse the values
|
|
265
|
+
* from the original request". An empty namespace and name are now a literal
|
|
266
|
+
* empty namespace and name.
|
|
267
|
+
*/
|
|
268
|
+
interface Redirect {
|
|
269
|
+
readonly connect_uri: string;
|
|
270
|
+
readonly track_namespace: string[];
|
|
271
|
+
readonly track_name: string;
|
|
272
|
+
}
|
|
273
|
+
interface Draft21RequestError extends Draft21BaseMessage {
|
|
274
|
+
readonly type: 'request_error';
|
|
275
|
+
readonly error_code: bigint;
|
|
276
|
+
readonly retry_interval: bigint;
|
|
277
|
+
readonly reason_phrase: string;
|
|
278
|
+
readonly redirect?: Redirect;
|
|
279
|
+
}
|
|
280
|
+
interface Draft21GoAway extends Draft21BaseMessage {
|
|
281
|
+
readonly type: 'goaway';
|
|
282
|
+
readonly new_session_uri: string;
|
|
283
|
+
readonly timeout: bigint;
|
|
284
|
+
}
|
|
285
|
+
type Draft21Message = Draft21Setup | Draft21Subscribe | Draft21SubscribeOk | Draft21RequestUpdate | Draft21PublishStateNotify | Draft21Publish | Draft21PublishDone | Draft21PublishNamespace | Draft21Namespace | Draft21NamespaceDone | Draft21SubscribeNamespace | Draft21SubscribeTracks | Draft21PublishSkipped | Draft21Fetch | Draft21FetchOk | Draft21TrackStatus | Draft21RequestOk | Draft21RequestError | Draft21GoAway;
|
|
286
|
+
interface ObjectPayload {
|
|
287
|
+
readonly type: 'object';
|
|
288
|
+
readonly byteOffset: number;
|
|
289
|
+
readonly payloadByteOffset: number;
|
|
290
|
+
readonly objectId: bigint;
|
|
291
|
+
/** The Object ID Delta as it appeared on the wire, before delta resolution. */
|
|
292
|
+
readonly objectIdDelta?: bigint;
|
|
293
|
+
readonly payloadLength: number;
|
|
294
|
+
readonly status?: bigint;
|
|
295
|
+
readonly extensionData: Uint8Array;
|
|
296
|
+
readonly objectProperties?: Record<string, bigint>;
|
|
297
|
+
readonly payload: Uint8Array;
|
|
298
|
+
}
|
|
299
|
+
interface SubgroupStream {
|
|
300
|
+
readonly type: 'subgroup';
|
|
301
|
+
/** SUBGROUP_HEADER `Type Flags` (draft-21 Section 11.3.1). */
|
|
302
|
+
readonly headerType: number;
|
|
303
|
+
readonly trackAlias: bigint;
|
|
304
|
+
readonly groupId: bigint;
|
|
305
|
+
readonly subgroupId: bigint;
|
|
306
|
+
readonly publisherPriority: number;
|
|
307
|
+
readonly endOfGroup?: boolean;
|
|
308
|
+
readonly firstObject?: boolean;
|
|
309
|
+
readonly objects: ObjectPayload[];
|
|
310
|
+
}
|
|
311
|
+
interface DatagramObject {
|
|
312
|
+
readonly type: 'datagram';
|
|
313
|
+
/** OBJECT_DATAGRAM `Type Flags` (draft-21 Section 11.2.1). */
|
|
314
|
+
readonly datagramType: number;
|
|
315
|
+
readonly trackAlias: bigint;
|
|
316
|
+
readonly groupId: bigint;
|
|
317
|
+
readonly objectId: bigint;
|
|
318
|
+
readonly publisherPriority: number;
|
|
319
|
+
readonly endOfGroup?: boolean;
|
|
320
|
+
readonly objectStatus?: bigint;
|
|
321
|
+
readonly objectProperties?: Record<string, bigint>;
|
|
322
|
+
readonly payloadLength: number;
|
|
323
|
+
readonly payload: Uint8Array;
|
|
324
|
+
}
|
|
325
|
+
interface FetchObjectPayload extends ObjectPayload {
|
|
326
|
+
readonly serializationFlags: number;
|
|
327
|
+
readonly groupId: bigint;
|
|
328
|
+
/** The Group ID Delta as it appeared on the wire, before delta resolution. */
|
|
329
|
+
readonly groupIdDelta?: bigint;
|
|
330
|
+
readonly subgroupId: bigint;
|
|
331
|
+
readonly publisherPriority: number;
|
|
332
|
+
readonly objectProperties?: Record<string, bigint>;
|
|
333
|
+
}
|
|
334
|
+
interface FetchStream {
|
|
335
|
+
readonly type: 'fetch';
|
|
336
|
+
/**
|
|
337
|
+
* The Request ID of the message that opened the stream. In draft-21 that is
|
|
338
|
+
* not necessarily a FETCH: a fill fetch stream (Section 3.4) carries the
|
|
339
|
+
* Request ID of the SUBSCRIBE or REQUEST_UPDATE that requested the fill.
|
|
340
|
+
*/
|
|
341
|
+
readonly requestId: bigint;
|
|
342
|
+
readonly objects: FetchObjectPayload[];
|
|
343
|
+
}
|
|
344
|
+
type Draft21DataStream = SubgroupStream | DatagramObject | FetchStream;
|
|
345
|
+
interface SubgroupStreamHeader {
|
|
346
|
+
readonly type: 'subgroup_header';
|
|
347
|
+
/**
|
|
348
|
+
* SUBGROUP_HEADER `Type Flags`, the same value `SubgroupStream.headerType`
|
|
349
|
+
* carries. It is reported so the incremental decoder does not drop the flags
|
|
350
|
+
* the header encodes, which is what lets a consumer tell the modes apart.
|
|
351
|
+
*/
|
|
352
|
+
readonly headerType: number;
|
|
353
|
+
readonly trackAlias: bigint;
|
|
354
|
+
readonly groupId: bigint;
|
|
355
|
+
/**
|
|
356
|
+
* Under Subgroup Mode 0b01 this is the first Object's ID, which is why the
|
|
357
|
+
* header is not emitted until that Object has been read.
|
|
358
|
+
*/
|
|
359
|
+
readonly subgroupId: bigint;
|
|
360
|
+
readonly publisherPriority: number;
|
|
361
|
+
readonly endOfGroup?: boolean;
|
|
362
|
+
readonly firstObject?: boolean;
|
|
363
|
+
}
|
|
364
|
+
interface FetchStreamHeader {
|
|
365
|
+
readonly type: 'fetch_header';
|
|
366
|
+
readonly requestId: bigint;
|
|
367
|
+
}
|
|
368
|
+
type DataStreamHeader = SubgroupStreamHeader | FetchStreamHeader;
|
|
369
|
+
type DataStreamEvent = DataStreamHeader | ObjectPayload;
|
|
370
|
+
|
|
371
|
+
export type { Draft21SubscribeNamespace as A, Draft21SubscribeOk as B, Draft21SubscribeTracks as C, Draft21Message as D, Draft21TrackProperties as E, FetchStreamHeader as F, Draft21TrackStatus as G, FetchObjectPayload as H, LocationFilter as I, RangeFilterRange as J, Redirect as K, LargestObject as L, ObjectPayload as O, RangeFilter as R, SubgroupStreamHeader as S, UnknownParam as U, Draft21MessageType as a, DataStreamEvent as b, Draft21DataStream as c, DatagramObject as d, FetchStream as e, SubgroupStream as f, DataStreamHeader as g, Draft21BaseMessage as h, Draft21Fetch as i, Draft21FetchOk as j, Draft21FillParameters as k, Draft21GoAway as l, Draft21Namespace as m, Draft21NamespaceDone as n, Draft21Params as o, Draft21Publish as p, Draft21PublishDone as q, Draft21PublishNamespace as r, Draft21PublishSkipped as s, Draft21PublishStateNotify as t, Draft21RequestError as u, Draft21RequestOk as v, Draft21RequestUpdate as w, Draft21Setup as x, Draft21SetupOptions as y, Draft21Subscribe as z };
|
|
@@ -234,10 +234,18 @@ interface FetchStream {
|
|
|
234
234
|
type Draft18DataStream = SubgroupStream | DatagramObject | FetchStream;
|
|
235
235
|
interface SubgroupStreamHeader {
|
|
236
236
|
readonly type: 'subgroup_header';
|
|
237
|
+
/**
|
|
238
|
+
* The stream Type Flags, the same value `SubgroupStream.headerType` carries.
|
|
239
|
+
* It is reported so the incremental decoder does not drop the flags the
|
|
240
|
+
* header encodes, which is what lets a consumer tell the modes apart.
|
|
241
|
+
*/
|
|
242
|
+
readonly headerType: number;
|
|
237
243
|
readonly trackAlias: bigint;
|
|
238
244
|
readonly groupId: bigint;
|
|
239
245
|
readonly subgroupId: bigint;
|
|
240
246
|
readonly publisherPriority: number;
|
|
247
|
+
readonly endOfGroup?: boolean;
|
|
248
|
+
readonly firstObject?: boolean;
|
|
241
249
|
}
|
|
242
250
|
interface FetchStreamHeader {
|
|
243
251
|
readonly type: 'fetch_header';
|
|
@@ -234,10 +234,18 @@ interface FetchStream {
|
|
|
234
234
|
type Draft18DataStream = SubgroupStream | DatagramObject | FetchStream;
|
|
235
235
|
interface SubgroupStreamHeader {
|
|
236
236
|
readonly type: 'subgroup_header';
|
|
237
|
+
/**
|
|
238
|
+
* The stream Type Flags, the same value `SubgroupStream.headerType` carries.
|
|
239
|
+
* It is reported so the incremental decoder does not drop the flags the
|
|
240
|
+
* header encodes, which is what lets a consumer tell the modes apart.
|
|
241
|
+
*/
|
|
242
|
+
readonly headerType: number;
|
|
237
243
|
readonly trackAlias: bigint;
|
|
238
244
|
readonly groupId: bigint;
|
|
239
245
|
readonly subgroupId: bigint;
|
|
240
246
|
readonly publisherPriority: number;
|
|
247
|
+
readonly endOfGroup?: boolean;
|
|
248
|
+
readonly firstObject?: boolean;
|
|
241
249
|
}
|
|
242
250
|
interface FetchStreamHeader {
|
|
243
251
|
readonly type: 'fetch_header';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moqtap/codec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "MoQT wire-format codec and session state machine — multi-draft, zero dependencies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -208,6 +208,18 @@
|
|
|
208
208
|
"types": "./dist/draft20-session.d.ts",
|
|
209
209
|
"import": "./dist/draft20-session.js",
|
|
210
210
|
"require": "./dist/draft20-session.cjs"
|
|
211
|
+
},
|
|
212
|
+
"./draft21": {
|
|
213
|
+
"bun": "./src/drafts/draft21/index.ts",
|
|
214
|
+
"types": "./dist/draft21.d.ts",
|
|
215
|
+
"import": "./dist/draft21.js",
|
|
216
|
+
"require": "./dist/draft21.cjs"
|
|
217
|
+
},
|
|
218
|
+
"./draft21/session": {
|
|
219
|
+
"bun": "./src/drafts/draft21/session.ts",
|
|
220
|
+
"types": "./dist/draft21-session.d.ts",
|
|
221
|
+
"import": "./dist/draft21-session.js",
|
|
222
|
+
"require": "./dist/draft21-session.cjs"
|
|
211
223
|
}
|
|
212
224
|
},
|
|
213
225
|
"files": [
|
|
@@ -221,9 +233,9 @@
|
|
|
221
233
|
"test": "vitest run"
|
|
222
234
|
},
|
|
223
235
|
"devDependencies": {
|
|
224
|
-
"@moqtap/test-vectors": "^0.
|
|
236
|
+
"@moqtap/test-vectors": "^0.17.0",
|
|
225
237
|
"tsup": "^8.0.0",
|
|
226
238
|
"typescript": "^5.5.0",
|
|
227
|
-
"vitest": "^3.
|
|
239
|
+
"vitest": "^3.2.7"
|
|
228
240
|
}
|
|
229
241
|
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overwriting Authorization Token values at the point of parse.
|
|
3
|
+
*
|
|
4
|
+
* A collector that ships a customer's control plane verbatim is useful — the
|
|
5
|
+
* control plane is the part of a session a player library cannot misreport —
|
|
6
|
+
* right up until it also ships the bearer token sitting in a SETUP option or a
|
|
7
|
+
* SUBSCRIBE parameter. At that point a debugging tool has become a
|
|
8
|
+
* credential-exfiltration path.
|
|
9
|
+
*
|
|
10
|
+
* The fix has to happen **where the parse happens**, not on the way out. Bytes
|
|
11
|
+
* that were merely *masked before upload* still sat in memory in a form a later
|
|
12
|
+
* feature — a dump, an export, a bug — could reach. So this module hangs off
|
|
13
|
+
* the decoders themselves: the one pass that reads a Token Value is the pass
|
|
14
|
+
* that records where it was, and the value is overwritten before the frame is
|
|
15
|
+
* handed to anything that retains it.
|
|
16
|
+
*
|
|
17
|
+
* ── What is overwritten, and what deliberately is not
|
|
18
|
+
*
|
|
19
|
+
* Only the **Token Value** (draft-20 Section 10.2.2, Figure 5). The Alias Type,
|
|
20
|
+
* the Token Alias and the Token Type all stay:
|
|
21
|
+
*
|
|
22
|
+
* - the parameter's *presence* distinguishes a session that failed to
|
|
23
|
+
* authenticate from one that never tried, and losing that distinction blinds
|
|
24
|
+
* the tool at exactly the moment it is most wanted;
|
|
25
|
+
* - the Token Alias is how you see the same credential being reused across
|
|
26
|
+
* messages, which is a real debugging signal and is not itself a secret;
|
|
27
|
+
* - `USE_ALIAS` and `DELETE` carry no value at all, so those frames pass
|
|
28
|
+
* through untouched and it is visible that they did.
|
|
29
|
+
*
|
|
30
|
+
* The overwrite is **length-preserving**. That is not an aesthetic choice: a
|
|
31
|
+
* control frame carries a fixed 16-bit Message Length, and every offset a
|
|
32
|
+
* caller has already computed into the frame stays valid only if the frame does
|
|
33
|
+
* not change size. Preserving the length also preserves the fact that a token
|
|
34
|
+
* of *some* size was sent.
|
|
35
|
+
*
|
|
36
|
+
* ── Why a span sink and not a return value
|
|
37
|
+
*
|
|
38
|
+
* A Token Value is read in `decodeParams` and `decodeSetupOptions`, which sit
|
|
39
|
+
* under a dozen per-message payload decoders per draft. Threading a redaction
|
|
40
|
+
* channel out through all of them would mean changing every one of those
|
|
41
|
+
* signatures, in every draft, to carry something almost none of them use.
|
|
42
|
+
*
|
|
43
|
+
* The sink is module state, which is safe here for one specific reason and it
|
|
44
|
+
* is worth being explicit about it: **decoding is synchronous and single
|
|
45
|
+
* threaded, and no decoder re-enters the redactor.** {@link redactWith} opens
|
|
46
|
+
* the capture, runs one decode to completion, and closes it in a `finally`.
|
|
47
|
+
* There is no `await` between those points, so no second capture can interleave
|
|
48
|
+
* with the first.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The byte written over a redacted Token Value.
|
|
53
|
+
*
|
|
54
|
+
* Zero, so that a redacted frame is obviously not a token rather than
|
|
55
|
+
* plausibly one. A caller who needs to tell "redacted" from "the peer really
|
|
56
|
+
* sent zeros" has {@link AuthRedaction.redacted}, which counts what was
|
|
57
|
+
* overwritten.
|
|
58
|
+
*/
|
|
59
|
+
export const REDACTION_FILL = 0x00
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The result of a redaction pass over one control frame.
|
|
63
|
+
*/
|
|
64
|
+
export interface AuthRedaction {
|
|
65
|
+
/**
|
|
66
|
+
* The frame with every Token Value overwritten, exactly as long as the input.
|
|
67
|
+
*
|
|
68
|
+
* When nothing needed redacting this is the **same reference** as the input,
|
|
69
|
+
* so the common case costs no copy. Callers that retain it must copy for the
|
|
70
|
+
* same reason they would have had to copy the input.
|
|
71
|
+
*/
|
|
72
|
+
readonly bytes: Uint8Array
|
|
73
|
+
/** How many Token Values were overwritten. */
|
|
74
|
+
readonly redacted: number
|
|
75
|
+
/**
|
|
76
|
+
* A span was recorded that could not be applied to the frame.
|
|
77
|
+
*
|
|
78
|
+
* This means the offset arithmetic and the frame disagree, which is a defect
|
|
79
|
+
* rather than a wire condition. **A caller must not treat {@link bytes} as
|
|
80
|
+
* clean when this is set** — the safe response is to drop the frame's raw
|
|
81
|
+
* bytes rather than ship them, because the one thing that is certain is that
|
|
82
|
+
* a token was seen and its position is not trusted.
|
|
83
|
+
*/
|
|
84
|
+
readonly incomplete: boolean
|
|
85
|
+
/**
|
|
86
|
+
* Whether the message parsed all the way through.
|
|
87
|
+
*
|
|
88
|
+
* A frame can be perfectly legitimate and still not decode: an unknown or
|
|
89
|
+
* extension message codepoint is something a peer is allowed to send, and a
|
|
90
|
+
* collector ships those raw on purpose rather than losing the whole control
|
|
91
|
+
* plane over one of them. But a message that cannot be parsed is a message
|
|
92
|
+
* whose parameters cannot be walked, so `redacted === 0` on such a frame
|
|
93
|
+
* means "nothing was found", not "there is nothing there".
|
|
94
|
+
*
|
|
95
|
+
* That residual is narrow and it is real, and the only dishonest thing to do
|
|
96
|
+
* with it is hide it. Count the frames where this is false, so the gap in the
|
|
97
|
+
* guarantee is a number somebody can look at.
|
|
98
|
+
*/
|
|
99
|
+
readonly decoded: boolean
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Payload-relative `[start, length]` pairs, flat. `null` when not capturing —
|
|
104
|
+
* which is the state every ordinary decode runs in, so the recording hook costs
|
|
105
|
+
* one null check on a path that is otherwise untouched.
|
|
106
|
+
*/
|
|
107
|
+
let spans: number[] | null = null
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Record that a Token Value occupies `length` bytes at `start`, measured from
|
|
111
|
+
* the beginning of the **payload** the current decoder is reading.
|
|
112
|
+
*
|
|
113
|
+
* Called from the draft decoders. A zero-length value records nothing: there is
|
|
114
|
+
* nothing to overwrite, and a zero-length span would only inflate the count.
|
|
115
|
+
*/
|
|
116
|
+
export function recordAuthValueSpan(start: number, length: number): void {
|
|
117
|
+
if (spans === null || length <= 0) return
|
|
118
|
+
spans.push(start, length)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Run `decode` with span capture suspended.
|
|
123
|
+
*
|
|
124
|
+
* For decoders that read a parameter block out of a **nested** buffer — draft-20's
|
|
125
|
+
* `FILL_PARAMETERS` is the case that exists today. Offsets recorded inside a
|
|
126
|
+
* nested reader are relative to that reader, and applying them to the outer
|
|
127
|
+
* payload would overwrite unrelated bytes: not a failed redaction but a
|
|
128
|
+
* corrupted frame, and a silent one. Scope rules already forbid an
|
|
129
|
+
* AUTHORIZATION_TOKEN inside FILL_PARAMETERS, so this guards against a future
|
|
130
|
+
* nesting rather than a present one — which is exactly when it is cheap to add.
|
|
131
|
+
*/
|
|
132
|
+
export function withoutAuthValueSpans<T>(decode: () => T): T {
|
|
133
|
+
const saved = spans
|
|
134
|
+
spans = null
|
|
135
|
+
try {
|
|
136
|
+
return decode()
|
|
137
|
+
} finally {
|
|
138
|
+
spans = saved
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Decode `frame` once, capturing Token Value spans, and return the frame with
|
|
144
|
+
* those values overwritten.
|
|
145
|
+
*
|
|
146
|
+
* `payloadStart` is where the message payload begins inside the frame; the
|
|
147
|
+
* spans the decoders record are payload-relative, and this is what makes them
|
|
148
|
+
* frame-relative. `decode` may fail — a malformed message decodes partially and
|
|
149
|
+
* throws or returns an error — and **spans recorded before it failed are still
|
|
150
|
+
* applied**, because a value that was read is a value that was exposed
|
|
151
|
+
* regardless of what happened afterwards.
|
|
152
|
+
*
|
|
153
|
+
* `decode` reports whether it parsed the whole message; a throw counts as no.
|
|
154
|
+
*/
|
|
155
|
+
export function redactWith(
|
|
156
|
+
frame: Uint8Array,
|
|
157
|
+
payloadStart: number,
|
|
158
|
+
decode: () => boolean,
|
|
159
|
+
): AuthRedaction {
|
|
160
|
+
const outer = spans
|
|
161
|
+
spans = []
|
|
162
|
+
let captured: number[]
|
|
163
|
+
let decoded = false
|
|
164
|
+
try {
|
|
165
|
+
decoded = decode()
|
|
166
|
+
} catch {
|
|
167
|
+
// The caller's decoder threw. Whatever it recorded before it did is real.
|
|
168
|
+
} finally {
|
|
169
|
+
captured = spans
|
|
170
|
+
spans = outer
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (captured.length === 0) return { bytes: frame, redacted: 0, incomplete: false, decoded }
|
|
174
|
+
|
|
175
|
+
let out: Uint8Array | null = null
|
|
176
|
+
let redacted = 0
|
|
177
|
+
let incomplete = false
|
|
178
|
+
for (let i = 0; i < captured.length; i += 2) {
|
|
179
|
+
const start = payloadStart + (captured[i] as number)
|
|
180
|
+
const length = captured[i + 1] as number
|
|
181
|
+
if (payloadStart < 0 || start < 0 || start + length > frame.length) {
|
|
182
|
+
incomplete = true
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
if (out === null) out = frame.slice()
|
|
186
|
+
out.fill(REDACTION_FILL, start, start + length)
|
|
187
|
+
redacted++
|
|
188
|
+
}
|
|
189
|
+
return { bytes: out ?? frame, redacted, incomplete, decoded }
|
|
190
|
+
}
|