@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
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,181 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
This file starts at 0.9.0. Earlier releases are in the git history.
|
|
9
9
|
|
|
10
|
+
## [0.12.0] - 2026-09-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **draft-21, as `@moqtap/codec/draft21` and `createCodec({ draft: '21' })`.**
|
|
15
|
+
Draft-21 restructures draft-20 and changes nothing on the wire: every code
|
|
16
|
+
point, every table row and every wire diagram is identical, so the codec, the
|
|
17
|
+
message and data-stream tables, the wire rules, the error registries and the
|
|
18
|
+
session FSM are draft-20's. The two are told apart only by what they
|
|
19
|
+
negotiate, `moqt-21` against `moqt-20`.
|
|
20
|
+
|
|
21
|
+
`message-type-names.test.ts` records that as a fact rather than a claim:
|
|
22
|
+
`20/21` joins the list of draft pairs whose whole name table coincides, which
|
|
23
|
+
until now held only drafts 08, 09 and 10.
|
|
24
|
+
|
|
25
|
+
What did move is where the draft says things -- 200 numbered sections became
|
|
26
|
+
214 and only 11 keep both their title and their number -- so every citation
|
|
27
|
+
in the new module is retargeted, two figure references with them. Requires
|
|
28
|
+
`@moqtap/test-vectors` 0.17.0, which adds the draft-21 corpus.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
|
|
32
|
+
- **A private path no longer ships in the package.** Three source files carried
|
|
33
|
+
a "Spec of record" reference to an internal planning document. `files`
|
|
34
|
+
includes `src`, and the generated `dist/*.d.ts` reproduced it, so the path was
|
|
35
|
+
in the published tarball. The comments now say why the code is as it is
|
|
36
|
+
without naming the document.
|
|
37
|
+
|
|
38
|
+
## [0.11.0] - 2026-09-17
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **`redactAuthTokens`, on all fourteen drafts.** Every draft entry point now
|
|
43
|
+
exports `redactAuthTokens`, with the `AuthRedaction` result type and the
|
|
44
|
+
`REDACTION_FILL` byte. It decodes a control frame for one purpose -- learning
|
|
45
|
+
where the credentials sat -- discards the message and returns the frame with
|
|
46
|
+
those bytes overwritten, so a caller never has to hold a frame containing one.
|
|
47
|
+
|
|
48
|
+
There are two credential shapes and the difference is not cosmetic. Drafts 07
|
|
49
|
+
through 10 have no Authorization Token: they carry AUTHORIZATION_INFO, a bare
|
|
50
|
+
UTF-8 string which *is* the bearer credential, so the whole parameter value
|
|
51
|
+
goes. From draft-11 only the Token Value is overwritten, and the Alias Type,
|
|
52
|
+
Token Alias and Token Type stay -- those are structure rather than secret, and
|
|
53
|
+
they are what distinguishes a session that failed to authenticate from one
|
|
54
|
+
that never tried.
|
|
55
|
+
|
|
56
|
+
A frame that fails to decode is still redacted as far as the decode reached,
|
|
57
|
+
which is the useful direction to fail in: a value that was read is a value
|
|
58
|
+
that was exposed, whatever went wrong after it. The result reports that case
|
|
59
|
+
as `incomplete`.
|
|
60
|
+
|
|
61
|
+
- **Every draft's error-code registries are reachable from its entry point.**
|
|
62
|
+
draft-17's were written, committed and left unexported, so the constants sat
|
|
63
|
+
in the published package with no way to name them from outside their own file.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- **draft-16 Setup Parameters are delta-encoded, and were read as absolute.**
|
|
68
|
+
draft-16 Section 1.4.2 introduced Key-Value-Pair Types carried as "a delta
|
|
69
|
+
from the previous Type value", with the Length-present-when-odd parity decided
|
|
70
|
+
by the *resolved* type. `decodeParams` had always done this; `decodeSetupParams`
|
|
71
|
+
never did. Drafts 07-15 are unaffected because delta encoding did not exist
|
|
72
|
+
yet, and 17-20 route their setup through the delta-aware decoder -- 16 was the
|
|
73
|
+
only hole.
|
|
74
|
+
|
|
75
|
+
**This leaked credentials rather than merely misreading them.** With absolute
|
|
76
|
+
types an AUTHORIZATION_TOKEN in any position but the first resolves to the
|
|
77
|
+
wrong type -- 0x03 reads as 0x01, PATH, which is also odd and whose Length
|
|
78
|
+
happens to cover the token exactly -- so the decode *completes*, no span is
|
|
79
|
+
recorded, and the frame comes back unredacted with `redacted: 0` and nothing
|
|
80
|
+
to notice. Found by pointing a real session at a public relay, whose
|
|
81
|
+
SERVER_SETUP ends where the frame does only under the delta reading.
|
|
82
|
+
|
|
83
|
+
- **draft-17's Token Value is not length-prefixed.** The encoder wrote, and the
|
|
84
|
+
decoder read, a varint length in front of the Token Value that draft-17 does
|
|
85
|
+
not define. Figure 5 writes the field as `Token Value (..)` -- the rest of the
|
|
86
|
+
value -- and the only length genuinely present is the Key-Value-Pair's own.
|
|
87
|
+
Draft-18 repeats that figure and its prose verbatim and already encoded none.
|
|
88
|
+
Because the redactor locates the credential from the outer length, the stray
|
|
89
|
+
byte also left draft-17 frames unredacted. Requires `@moqtap/test-vectors`
|
|
90
|
+
0.16.0, which drops the same byte from ten vectors.
|
|
91
|
+
|
|
92
|
+
- **A status Object no longer desynchronises the streaming decoders.** draft-20
|
|
93
|
+
Section 11.4.2: "The Object Status field is only sent if the Object Payload
|
|
94
|
+
Length is zero." The streaming subgroup transforms read the length, saw zero
|
|
95
|
+
and went to the next Object, leaving the status varint to be taken for the
|
|
96
|
+
next Object's Object ID Delta -- so one status Object silently decoded every
|
|
97
|
+
Object after it as garbage. Drafts 15, 17, 18, 19 and 20 were affected, and
|
|
98
|
+
draft-15's fetch decoder with them; each draft's one-shot decoder always had
|
|
99
|
+
the branch.
|
|
100
|
+
|
|
101
|
+
- **`bun run typecheck` no longer overwrites the published bundle.** tsup owns
|
|
102
|
+
`dist`, and every package `tsconfig.json` pointed `outDir` at the same
|
|
103
|
+
directory, so a local `build` followed by `typecheck` replaced the bundle with
|
|
104
|
+
an unbundled compilation plus a nested tree -- and `.npmignore` excludes
|
|
105
|
+
`dist/*/`, so the tarball kept the entry point and dropped the subtree it
|
|
106
|
+
imported. The typecheck program now emits nothing. Published tarballs were
|
|
107
|
+
unaffected only because both CI workflows happen to run `typecheck` before
|
|
108
|
+
`build`; any local `build -> typecheck -> publish` shipped it.
|
|
109
|
+
|
|
110
|
+
- **The streaming decoders and the one-shot decoders now agree.** Every draft
|
|
111
|
+
from 14 to 20 ships two ways to read a data stream, and nothing had ever
|
|
112
|
+
compared them. They had drifted, in six ways, none of which corrupted a
|
|
113
|
+
payload -- which is why it survived. A consumer that switched paths got
|
|
114
|
+
objects that looked right and carried a different shape.
|
|
115
|
+
|
|
116
|
+
On subgroup streams: the incremental decoder returned Properties as opaque
|
|
117
|
+
`extensionData` where the one-shot parsed them into `objectProperties`
|
|
118
|
+
(drafts 17-20); it dropped `headerType`, `endOfGroup` and `firstObject`
|
|
119
|
+
entirely; it left the Subgroup ID at zero under the mode that derives it from
|
|
120
|
+
the first Object's ID, rather than at that ID; and it reported every Object
|
|
121
|
+
as `byteOffset: 0`.
|
|
122
|
+
|
|
123
|
+
On fetch streams it was worse. The incremental decoder returned a bare
|
|
124
|
+
`ObjectPayload` with no `serializationFlags`, no `groupId`, no `subgroupId`,
|
|
125
|
+
no `publisherPriority` and no deltas, and because it carried no state between
|
|
126
|
+
Objects, **DATAGRAM-mode Object IDs were simply wrong** -- 0, 0, 0 where the
|
|
127
|
+
one-shot resolved 1, 2, 3.
|
|
128
|
+
|
|
129
|
+
Both decoders now share one header reader and one object reader per draft,
|
|
130
|
+
lifted out of the one-shot unchanged, so there is no second copy left to
|
|
131
|
+
drift. A new cross-draft test sweeps every header type and every
|
|
132
|
+
Serialization Flags combination each draft accepts and requires the two paths
|
|
133
|
+
to agree, whole-buffer and one byte at a time.
|
|
134
|
+
|
|
135
|
+
- **`createDataStreamDecoder` streams.** It picked an inner decoder and then
|
|
136
|
+
never wrote a byte to it: chunks accumulated in a local buffer and `flush`
|
|
137
|
+
decoded the whole stream in one shot, so every event arrived at end of
|
|
138
|
+
stream. On a subscription that stays open that is no events at all. It now
|
|
139
|
+
pumps the inner decoder's output as the bytes arrive. Which first bytes
|
|
140
|
+
select which decoder is unchanged.
|
|
141
|
+
|
|
142
|
+
### Changed
|
|
143
|
+
|
|
144
|
+
- **Breaking, at the type level.** `SubgroupStreamHeader` gains a required
|
|
145
|
+
`headerType`, and `endOfGroup` and `firstObject` where the draft has them.
|
|
146
|
+
Code that reads the header event is unaffected; code that constructs one has
|
|
147
|
+
a field to add. The value is the same one `SubgroupStream.headerType`
|
|
148
|
+
carries.
|
|
149
|
+
|
|
150
|
+
Under the Subgroup ID mode that takes the ID from the first Object, the
|
|
151
|
+
header event is now emitted after that Object has been read rather than
|
|
152
|
+
before, because that is the first moment its Subgroup ID is known. It still
|
|
153
|
+
arrives ahead of every Object in the output.
|
|
154
|
+
|
|
155
|
+
### Performance
|
|
156
|
+
|
|
157
|
+
- **The streaming decoders stop recopying their buffer on every chunk.** Both
|
|
158
|
+
allocated an array of `unread + chunk.length` and copied both halves into it
|
|
159
|
+
per chunk, which is quadratic in chunks per stream, and both built a fresh
|
|
160
|
+
subarray and `BufferReader` per *object*. Capacity now grows until reclaiming
|
|
161
|
+
the consumed prefix would leave the buffer at least half free, and never
|
|
162
|
+
shrinks; the reader is hoisted out of the object loop.
|
|
163
|
+
|
|
164
|
+
The buffer never compacts in place, and that is load-bearing rather than
|
|
165
|
+
incidental: payloads are handed out as views into it, so moving bytes within
|
|
166
|
+
it would corrupt an Object already emitted. The consumed prefix is dropped
|
|
167
|
+
only when a fresh array is being allocated anyway, which keeps the old array
|
|
168
|
+
alive exactly as long as the views into it.
|
|
169
|
+
|
|
170
|
+
Four megabytes, best of five, previous strategy against this one:
|
|
171
|
+
|
|
172
|
+
| objects | chunks | before | after | |
|
|
173
|
+
| ------- | ------ | ------ | ----- | ----- |
|
|
174
|
+
| 6 KB | 16 KB | 0.6 ms | 0.7 ms | 0.8x |
|
|
175
|
+
| 6 KB | 1 KB | 2.5 ms | 1.2 ms | 2.0x |
|
|
176
|
+
| 120 KB | 16 KB | 2.5 ms | 0.7 ms | 3.5x |
|
|
177
|
+
| 120 KB | 1 KB | 27.0 ms | 0.8 ms | 32.1x |
|
|
178
|
+
|
|
179
|
+
The first row is the shape the T10 captures have, and it is why a browser
|
|
180
|
+
measurement of this moved not at all: at 6 KB objects against 16 KB chunks
|
|
181
|
+
almost no chunk has an unread remainder to recopy. The last row is the same
|
|
182
|
+
code on objects those captures do contain -- they reach 120 KB -- arriving in
|
|
183
|
+
small chunks.
|
|
184
|
+
|
|
10
185
|
## [0.10.0] - 2026-09-03
|
|
11
186
|
|
|
12
187
|
### Changed
|
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ Each draft is available as a subpath import with its own codec and session state
|
|
|
71
71
|
| `@moqtap/codec/draft18` | Draft-18 codec |
|
|
72
72
|
| `@moqtap/codec/draft19` | Draft-19 codec |
|
|
73
73
|
| `@moqtap/codec/draft20` | Draft-20 codec |
|
|
74
|
+
| `@moqtap/codec/draft21` | Draft-21 codec |
|
|
74
75
|
| `@moqtap/codec/draft{N}/session` | Session state machine for draft N |
|
|
75
76
|
|
|
76
77
|
> **Note:** A default (versionless) codec will be available once the MoQT specification reaches RFC status. Until then, always specify a draft version.
|
|
@@ -78,11 +79,11 @@ Each draft is available as a subpath import with its own codec and session state
|
|
|
78
79
|
`DRAFT_VERSIONS` is a table of numeric keys, and only the entries up to `'14'` are values a peer
|
|
79
80
|
ever puts on the wire. From draft-15 the version is negotiated by ALPN (raw QUIC) or
|
|
80
81
|
`WT-Available-Protocols` (WebTransport) as the string `moqt-NN`, and no version number is sent at
|
|
81
|
-
all — so `DRAFT_VERSIONS['
|
|
82
|
+
all — so `DRAFT_VERSIONS['21']` is a derived identifier, not something observed. Each draft module
|
|
82
83
|
from 15 on exports the real one:
|
|
83
84
|
|
|
84
85
|
```typescript
|
|
85
|
-
import { PROTOCOL_STRING } from '@moqtap/codec/
|
|
86
|
+
import { PROTOCOL_STRING } from '@moqtap/codec/draft21' // 'moqt-21'
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
## Draft-Specific Imports
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
* The byte written over a redacted Token Value.
|
|
52
|
+
*
|
|
53
|
+
* Zero, so that a redacted frame is obviously not a token rather than
|
|
54
|
+
* plausibly one. A caller who needs to tell "redacted" from "the peer really
|
|
55
|
+
* sent zeros" has {@link AuthRedaction.redacted}, which counts what was
|
|
56
|
+
* overwritten.
|
|
57
|
+
*/
|
|
58
|
+
declare const REDACTION_FILL = 0;
|
|
59
|
+
/**
|
|
60
|
+
* The result of a redaction pass over one control frame.
|
|
61
|
+
*/
|
|
62
|
+
interface AuthRedaction {
|
|
63
|
+
/**
|
|
64
|
+
* The frame with every Token Value overwritten, exactly as long as the input.
|
|
65
|
+
*
|
|
66
|
+
* When nothing needed redacting this is the **same reference** as the input,
|
|
67
|
+
* so the common case costs no copy. Callers that retain it must copy for the
|
|
68
|
+
* same reason they would have had to copy the input.
|
|
69
|
+
*/
|
|
70
|
+
readonly bytes: Uint8Array;
|
|
71
|
+
/** How many Token Values were overwritten. */
|
|
72
|
+
readonly redacted: number;
|
|
73
|
+
/**
|
|
74
|
+
* A span was recorded that could not be applied to the frame.
|
|
75
|
+
*
|
|
76
|
+
* This means the offset arithmetic and the frame disagree, which is a defect
|
|
77
|
+
* rather than a wire condition. **A caller must not treat {@link bytes} as
|
|
78
|
+
* clean when this is set** — the safe response is to drop the frame's raw
|
|
79
|
+
* bytes rather than ship them, because the one thing that is certain is that
|
|
80
|
+
* a token was seen and its position is not trusted.
|
|
81
|
+
*/
|
|
82
|
+
readonly incomplete: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Whether the message parsed all the way through.
|
|
85
|
+
*
|
|
86
|
+
* A frame can be perfectly legitimate and still not decode: an unknown or
|
|
87
|
+
* extension message codepoint is something a peer is allowed to send, and a
|
|
88
|
+
* collector ships those raw on purpose rather than losing the whole control
|
|
89
|
+
* plane over one of them. But a message that cannot be parsed is a message
|
|
90
|
+
* whose parameters cannot be walked, so `redacted === 0` on such a frame
|
|
91
|
+
* means "nothing was found", not "there is nothing there".
|
|
92
|
+
*
|
|
93
|
+
* That residual is narrow and it is real, and the only dishonest thing to do
|
|
94
|
+
* with it is hide it. Count the frames where this is false, so the gap in the
|
|
95
|
+
* guarantee is a number somebody can look at.
|
|
96
|
+
*/
|
|
97
|
+
readonly decoded: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export { type AuthRedaction as A, REDACTION_FILL as R };
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
* The byte written over a redacted Token Value.
|
|
52
|
+
*
|
|
53
|
+
* Zero, so that a redacted frame is obviously not a token rather than
|
|
54
|
+
* plausibly one. A caller who needs to tell "redacted" from "the peer really
|
|
55
|
+
* sent zeros" has {@link AuthRedaction.redacted}, which counts what was
|
|
56
|
+
* overwritten.
|
|
57
|
+
*/
|
|
58
|
+
declare const REDACTION_FILL = 0;
|
|
59
|
+
/**
|
|
60
|
+
* The result of a redaction pass over one control frame.
|
|
61
|
+
*/
|
|
62
|
+
interface AuthRedaction {
|
|
63
|
+
/**
|
|
64
|
+
* The frame with every Token Value overwritten, exactly as long as the input.
|
|
65
|
+
*
|
|
66
|
+
* When nothing needed redacting this is the **same reference** as the input,
|
|
67
|
+
* so the common case costs no copy. Callers that retain it must copy for the
|
|
68
|
+
* same reason they would have had to copy the input.
|
|
69
|
+
*/
|
|
70
|
+
readonly bytes: Uint8Array;
|
|
71
|
+
/** How many Token Values were overwritten. */
|
|
72
|
+
readonly redacted: number;
|
|
73
|
+
/**
|
|
74
|
+
* A span was recorded that could not be applied to the frame.
|
|
75
|
+
*
|
|
76
|
+
* This means the offset arithmetic and the frame disagree, which is a defect
|
|
77
|
+
* rather than a wire condition. **A caller must not treat {@link bytes} as
|
|
78
|
+
* clean when this is set** — the safe response is to drop the frame's raw
|
|
79
|
+
* bytes rather than ship them, because the one thing that is certain is that
|
|
80
|
+
* a token was seen and its position is not trusted.
|
|
81
|
+
*/
|
|
82
|
+
readonly incomplete: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Whether the message parsed all the way through.
|
|
85
|
+
*
|
|
86
|
+
* A frame can be perfectly legitimate and still not decode: an unknown or
|
|
87
|
+
* extension message codepoint is something a peer is allowed to send, and a
|
|
88
|
+
* collector ships those raw on purpose rather than losing the whole control
|
|
89
|
+
* plane over one of them. But a message that cannot be parsed is a message
|
|
90
|
+
* whose parameters cannot be walked, so `redacted === 0` on such a frame
|
|
91
|
+
* means "nothing was found", not "there is nothing there".
|
|
92
|
+
*
|
|
93
|
+
* That residual is narrow and it is real, and the only dishonest thing to do
|
|
94
|
+
* with it is hide it. Count the frames where this is false, so the gap in the
|
|
95
|
+
* guarantee is a number somebody can look at.
|
|
96
|
+
*/
|
|
97
|
+
readonly decoded: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export { type AuthRedaction as A, REDACTION_FILL as R };
|