@moqtap/codec 0.10.0 → 0.11.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.
Files changed (213) hide show
  1. package/CHANGELOG.md +147 -0
  2. package/dist/auth-redaction-DsdLq4Qs.d.cts +102 -0
  3. package/dist/auth-redaction-DsdLq4Qs.d.ts +102 -0
  4. package/dist/{chunk-RYKQXBG6.cjs → chunk-27K5BUAG.cjs} +481 -517
  5. package/dist/{chunk-WCPENYNE.js → chunk-3RNQMGKA.js} +359 -390
  6. package/dist/{chunk-TSUZ6GJV.js → chunk-5AWIXOLB.js} +438 -469
  7. package/dist/{chunk-4XLYCT5Q.cjs → chunk-5IMHJIAZ.cjs} +52 -2
  8. package/dist/{chunk-KTNERCTZ.cjs → chunk-5JGZCKJO.cjs} +488 -518
  9. package/dist/{chunk-WCTMPDUX.cjs → chunk-73K4OSAN.cjs} +401 -432
  10. package/dist/{chunk-N2V7Z37J.js → chunk-7VQKRGRD.js} +19 -3
  11. package/dist/{chunk-BIBMDTU5.cjs → chunk-A3UCQOIB.cjs} +486 -517
  12. package/dist/{chunk-K2B3OVFK.js → chunk-A7PDMVXM.js} +24 -3
  13. package/dist/{chunk-VZHL2U2N.cjs → chunk-A7VQO6YC.cjs} +54 -38
  14. package/dist/{chunk-AQ445VV7.cjs → chunk-ACQHZLRZ.cjs} +66 -46
  15. package/dist/{chunk-L5DRACXS.js → chunk-BBICOMW6.js} +25 -3
  16. package/dist/{chunk-T4VWPKDG.cjs → chunk-BNIUVOCJ.cjs} +529 -599
  17. package/dist/{chunk-UKKDOOUO.cjs → chunk-CNC6ANS4.cjs} +58 -37
  18. package/dist/{chunk-OZRK73NQ.js → chunk-FDLQSWHK.js} +433 -469
  19. package/dist/{chunk-IJAF6PAY.js → chunk-FFNOE4UR.js} +19 -3
  20. package/dist/{chunk-CZNW3R5E.cjs → chunk-GMENT63T.cjs} +54 -38
  21. package/dist/{chunk-XCRCAUGI.cjs → chunk-HM7BLRVP.cjs} +291 -297
  22. package/dist/{chunk-XHAP2YST.js → chunk-HZONHFNO.js} +19 -3
  23. package/dist/{chunk-SDJTSMOW.cjs → chunk-ILYABWDK.cjs} +60 -38
  24. package/dist/{chunk-IDV2JYJ6.cjs → chunk-KRSFTZVO.cjs} +56 -40
  25. package/dist/{chunk-RIIA4ALQ.cjs → chunk-KSPH6KMH.cjs} +54 -38
  26. package/dist/{chunk-OUNN4IQB.cjs → chunk-KTY4LAUV.cjs} +482 -497
  27. package/dist/{chunk-RZTLQAKF.js → chunk-OJNLJT5D.js} +0 -1
  28. package/dist/{chunk-FT3HDNIB.js → chunk-PJK2ZS3I.js} +467 -537
  29. package/dist/{chunk-G53WYUXL.js → chunk-PSMZKHOC.js} +441 -456
  30. package/dist/{chunk-7RE6KSIY.cjs → chunk-Q5JQGHWY.cjs} +0 -1
  31. package/dist/{chunk-BX2N7TZM.js → chunk-SQTUQY5E.js} +31 -11
  32. package/dist/{chunk-CF5DDD3Q.js → chunk-WCRLAKDW.js} +239 -245
  33. package/dist/{chunk-KYVTM5VI.js → chunk-WL2YUEXH.js} +19 -3
  34. package/dist/{chunk-KGDVC4O6.js → chunk-YCNIVZS7.js} +439 -469
  35. package/dist/{chunk-KLPWJU6W.js → chunk-YRZQVNZO.js} +50 -0
  36. package/dist/{codec-CFWu5KVT.d.cts → codec-76xzrLTL.d.cts} +21 -2
  37. package/dist/{codec-gl4_QEva.d.ts → codec-8p40WdLg.d.ts} +20 -1
  38. package/dist/{codec-Dl_QIXMz.d.ts → codec-BN2Bu0KI.d.ts} +20 -1
  39. package/dist/{codec-Bux_Q40X.d.ts → codec-Bb6GKTrv.d.ts} +21 -2
  40. package/dist/{codec-EvybrBuY.d.cts → codec-BgpfHoAd.d.cts} +20 -1
  41. package/dist/{codec-alVA27Uc.d.ts → codec-BkheTXO7.d.ts} +20 -1
  42. package/dist/{codec-Czejcmxr.d.cts → codec-BwVDkokh.d.cts} +21 -2
  43. package/dist/{codec-BrTi4K7r.d.cts → codec-C5s1eBJX.d.cts} +21 -2
  44. package/dist/codec-CM5yd6n3.d.ts +38 -0
  45. package/dist/{codec-CWnf8mn_.d.cts → codec-Cb8HC3XV.d.cts} +21 -2
  46. package/dist/codec-Clu_skx8.d.cts +38 -0
  47. package/dist/{codec-DzkdW4kO.d.cts → codec-CpOpemR8.d.cts} +20 -1
  48. package/dist/{codec-CXCjya_G.d.cts → codec-Cshdl9Tt.d.cts} +20 -1
  49. package/dist/{codec-f7vxeVIS.d.cts → codec-DELe00w_.d.cts} +21 -2
  50. package/dist/{codec-DNmiG2e0.d.cts → codec-DHzKAUCa.d.cts} +20 -1
  51. package/dist/{codec--CVeqGQv.d.ts → codec-DdVBtvx7.d.ts} +20 -1
  52. package/dist/{codec-CBdOIfMY.d.ts → codec-DlxGuYMv.d.ts} +21 -2
  53. package/dist/{codec-BNy5syCU.d.ts → codec-DxtgPxJS.d.ts} +20 -1
  54. package/dist/{codec-guzqtfMk.d.cts → codec-GAY4lybi.d.cts} +20 -1
  55. package/dist/{codec-Cfo_z5ET.d.cts → codec-I8u2pfS4.d.cts} +20 -1
  56. package/dist/{codec-BxQTybUX.d.cts → codec-XLrnreeu.d.cts} +21 -2
  57. package/dist/{codec-DOmAmFkI.d.ts → codec-c0YEwwWn.d.ts} +20 -1
  58. package/dist/{codec-DaXjRrOV.d.ts → codec-ejDu-va6.d.ts} +21 -2
  59. package/dist/{codec-C3mdsGx3.d.ts → codec-kaBUzdpV.d.ts} +21 -2
  60. package/dist/{codec-CwZ6OCPP.d.ts → codec-mVYqkIUF.d.ts} +21 -2
  61. package/dist/{codec-08ZOr5wI.d.ts → codec-qSbO5nOa.d.ts} +21 -2
  62. package/dist/{codec-Cm3D261p.d.ts → codec-yYKGOipj.d.ts} +20 -1
  63. package/dist/{codec-Bd60SGKH.d.cts → codec-zfzg9uiy.d.cts} +20 -1
  64. package/dist/draft07.cjs +48 -4
  65. package/dist/draft07.d.cts +42 -2
  66. package/dist/draft07.d.ts +42 -2
  67. package/dist/draft07.js +49 -5
  68. package/dist/draft08.cjs +76 -3
  69. package/dist/draft08.d.cts +70 -2
  70. package/dist/draft08.d.ts +70 -2
  71. package/dist/draft08.js +77 -4
  72. package/dist/draft09.cjs +76 -3
  73. package/dist/draft09.d.cts +70 -2
  74. package/dist/draft09.d.ts +70 -2
  75. package/dist/draft09.js +77 -4
  76. package/dist/draft10.cjs +76 -3
  77. package/dist/draft10.d.cts +70 -2
  78. package/dist/draft10.d.ts +70 -2
  79. package/dist/draft10.js +77 -4
  80. package/dist/draft11.cjs +104 -3
  81. package/dist/draft11.d.cts +98 -2
  82. package/dist/draft11.d.ts +98 -2
  83. package/dist/draft11.js +105 -4
  84. package/dist/draft12.cjs +113 -3
  85. package/dist/draft12.d.cts +107 -2
  86. package/dist/draft12.d.ts +107 -2
  87. package/dist/draft12.js +114 -4
  88. package/dist/draft13.cjs +105 -3
  89. package/dist/draft13.d.cts +99 -2
  90. package/dist/draft13.d.ts +99 -2
  91. package/dist/draft13.js +106 -4
  92. package/dist/draft14.cjs +107 -3
  93. package/dist/draft14.d.cts +101 -28
  94. package/dist/draft14.d.ts +101 -28
  95. package/dist/draft14.js +108 -4
  96. package/dist/draft15-session.d.cts +1 -1
  97. package/dist/draft15-session.d.ts +1 -1
  98. package/dist/draft15.cjs +69 -3
  99. package/dist/draft15.d.cts +65 -22
  100. package/dist/draft15.d.ts +65 -22
  101. package/dist/draft15.js +70 -4
  102. package/dist/draft16-session.cjs +2 -2
  103. package/dist/draft16-session.d.cts +1 -1
  104. package/dist/draft16-session.d.ts +1 -1
  105. package/dist/draft16-session.js +1 -1
  106. package/dist/draft16.cjs +76 -8
  107. package/dist/draft16.d.cts +67 -13
  108. package/dist/draft16.d.ts +67 -13
  109. package/dist/draft16.js +73 -5
  110. package/dist/draft17-session.d.cts +1 -1
  111. package/dist/draft17-session.d.ts +1 -1
  112. package/dist/draft17.cjs +77 -3
  113. package/dist/draft17.d.cts +73 -4
  114. package/dist/draft17.d.ts +73 -4
  115. package/dist/draft17.js +78 -4
  116. package/dist/draft18-session.d.cts +1 -1
  117. package/dist/draft18-session.d.ts +1 -1
  118. package/dist/draft18.cjs +8 -3
  119. package/dist/draft18.d.cts +4 -3
  120. package/dist/draft18.d.ts +4 -3
  121. package/dist/draft18.js +9 -4
  122. package/dist/draft19-session.d.cts +1 -1
  123. package/dist/draft19-session.d.ts +1 -1
  124. package/dist/draft19.cjs +11 -4
  125. package/dist/draft19.d.cts +4 -3
  126. package/dist/draft19.d.ts +4 -3
  127. package/dist/draft19.js +12 -5
  128. package/dist/draft20-session.d.cts +1 -1
  129. package/dist/draft20-session.d.ts +1 -1
  130. package/dist/draft20.cjs +8 -3
  131. package/dist/draft20.d.cts +4 -3
  132. package/dist/draft20.d.ts +4 -3
  133. package/dist/draft20.js +9 -4
  134. package/dist/index.cjs +30 -30
  135. package/dist/index.d.cts +21 -20
  136. package/dist/index.d.ts +21 -20
  137. package/dist/index.js +15 -15
  138. package/dist/session.cjs +2 -2
  139. package/dist/session.d.cts +6 -6
  140. package/dist/session.d.ts +6 -6
  141. package/dist/session.js +1 -1
  142. package/dist/{types-C1rxh-id.d.cts → types-Btp416jt.d.cts} +8 -0
  143. package/dist/{types-C1rxh-id.d.ts → types-Btp416jt.d.ts} +8 -0
  144. package/dist/{types-CZTzbIzQ.d.cts → types-BxTL1NXw.d.cts} +7 -0
  145. package/dist/{types-CZTzbIzQ.d.ts → types-BxTL1NXw.d.ts} +7 -0
  146. package/dist/{types-pShrs3fN.d.cts → types-CuibHYcf.d.cts} +7 -0
  147. package/dist/{types-pShrs3fN.d.ts → types-CuibHYcf.d.ts} +7 -0
  148. package/dist/{types-CY-QWxd-.d.cts → types-CwN7AEoL.d.cts} +13 -1
  149. package/dist/{types-CY-QWxd-.d.ts → types-CwN7AEoL.d.ts} +13 -1
  150. package/dist/{types-D4bmW8rY.d.cts → types-DAnalt5K.d.cts} +7 -0
  151. package/dist/{types-D4bmW8rY.d.ts → types-DAnalt5K.d.ts} +7 -0
  152. package/dist/{types-Bg86GGVZ.d.cts → types-DxXNP32l.d.cts} +8 -0
  153. package/dist/{types-Bg86GGVZ.d.ts → types-DxXNP32l.d.ts} +8 -0
  154. package/package.json +3 -3
  155. package/src/core/auth-redaction.ts +192 -0
  156. package/src/drafts/draft07/codec.ts +40 -0
  157. package/src/drafts/draft07/error-codes.ts +44 -0
  158. package/src/drafts/draft07/index.ts +15 -0
  159. package/src/drafts/draft08/codec.ts +40 -0
  160. package/src/drafts/draft08/error-codes.ts +76 -0
  161. package/src/drafts/draft08/index.ts +21 -0
  162. package/src/drafts/draft09/codec.ts +40 -0
  163. package/src/drafts/draft09/error-codes.ts +76 -0
  164. package/src/drafts/draft09/index.ts +21 -0
  165. package/src/drafts/draft10/codec.ts +40 -0
  166. package/src/drafts/draft10/error-codes.ts +76 -0
  167. package/src/drafts/draft10/index.ts +21 -0
  168. package/src/drafts/draft11/codec.ts +60 -0
  169. package/src/drafts/draft11/error-codes.ts +106 -0
  170. package/src/drafts/draft11/index.ts +23 -0
  171. package/src/drafts/draft12/codec.ts +61 -0
  172. package/src/drafts/draft12/error-codes.ts +116 -0
  173. package/src/drafts/draft12/index.ts +25 -0
  174. package/src/drafts/draft13/codec.ts +69 -11
  175. package/src/drafts/draft13/error-codes.ts +107 -0
  176. package/src/drafts/draft13/index.ts +23 -0
  177. package/src/drafts/draft13/types.ts +1 -1
  178. package/src/drafts/draft14/codec.ts +65 -4
  179. package/src/drafts/draft14/data-streams.ts +331 -259
  180. package/src/drafts/draft14/error-codes.ts +109 -0
  181. package/src/drafts/draft14/index.ts +23 -0
  182. package/src/drafts/draft15/codec.ts +65 -8
  183. package/src/drafts/draft15/data-streams.ts +489 -444
  184. package/src/drafts/draft15/error-codes.ts +66 -0
  185. package/src/drafts/draft15/index.ts +15 -0
  186. package/src/drafts/draft15/types.ts +7 -0
  187. package/src/drafts/draft16/codec.ts +146 -52
  188. package/src/drafts/draft16/data-streams.ts +513 -442
  189. package/src/drafts/draft16/error-codes.ts +68 -0
  190. package/src/drafts/draft16/index.ts +15 -0
  191. package/src/drafts/draft16/messages.ts +4 -4
  192. package/src/drafts/draft16/session-fsm.ts +0 -1
  193. package/src/drafts/draft16/types.ts +22 -8
  194. package/src/drafts/draft17/codec.ts +73 -12
  195. package/src/drafts/draft17/data-streams.ts +567 -488
  196. package/src/drafts/draft17/index.ts +15 -0
  197. package/src/drafts/draft17/types.ts +7 -0
  198. package/src/drafts/draft18/codec.ts +66 -9
  199. package/src/drafts/draft18/data-streams.ts +570 -482
  200. package/src/drafts/draft18/index.ts +3 -0
  201. package/src/drafts/draft18/types.ts +16 -8
  202. package/src/drafts/draft19/codec.ts +70 -10
  203. package/src/drafts/draft19/data-streams.ts +570 -482
  204. package/src/drafts/draft19/error-codes.ts +3 -1
  205. package/src/drafts/draft19/index.ts +3 -0
  206. package/src/drafts/draft19/messages.ts +3 -1
  207. package/src/drafts/draft19/types.ts +22 -11
  208. package/src/drafts/draft20/codec.ts +90 -23
  209. package/src/drafts/draft20/data-streams.ts +610 -563
  210. package/src/drafts/draft20/index.ts +3 -0
  211. package/src/drafts/draft20/types.ts +14 -2
  212. package/dist/codec-BJ7BHuiZ.d.ts +0 -18
  213. package/dist/codec-C6Mx4bwz.d.cts +0 -18
@@ -0,0 +1,66 @@
1
+ // Draft-15 error, status and termination code registries.
2
+
3
+ export const SessionErrorCode = {
4
+ NoError: 0x0n,
5
+ InternalError: 0x1n,
6
+ Unauthorized: 0x2n,
7
+ ProtocolViolation: 0x3n,
8
+ InvalidRequestId: 0x4n,
9
+ DuplicateTrackAlias: 0x5n,
10
+ KeyValueFormattingError: 0x6n,
11
+ TooManyRequests: 0x7n,
12
+ InvalidPath: 0x8n,
13
+ MalformedPath: 0x9n,
14
+ GoawayTimeout: 0x10n,
15
+ ControlMessageTimeout: 0x11n,
16
+ DataStreamTimeout: 0x12n,
17
+ AuthTokenCacheOverflow: 0x13n,
18
+ DuplicateAuthTokenAlias: 0x14n,
19
+ VersionNegotiationFailed: 0x15n,
20
+ MalformedAuthToken: 0x16n,
21
+ UnknownAuthTokenAlias: 0x17n,
22
+ ExpiredAuthToken: 0x18n,
23
+ InvalidAuthority: 0x19n,
24
+ MalformedAuthority: 0x1an,
25
+ } as const
26
+ export type SessionErrorCodeValue = (typeof SessionErrorCode)[keyof typeof SessionErrorCode]
27
+
28
+ export const RequestErrorCode = {
29
+ InternalError: 0x0n,
30
+ Unauthorized: 0x1n,
31
+ Timeout: 0x2n,
32
+ NotSupported: 0x3n,
33
+ MalformedAuthToken: 0x4n,
34
+ ExpiredAuthToken: 0x5n,
35
+ DoesNotExist: 0x10n,
36
+ InvalidRange: 0x11n,
37
+ MalformedTrack: 0x12n,
38
+ Uninterested: 0x20n,
39
+ PrefixOverlap: 0x30n,
40
+ InvalidJoiningRequestId: 0x32n,
41
+ UnknownStatusInRange: 0x33n,
42
+ } as const
43
+ export type RequestErrorCodeValue = (typeof RequestErrorCode)[keyof typeof RequestErrorCode]
44
+
45
+ export const PublishDoneStatusCode = {
46
+ InternalError: 0x0n,
47
+ Unauthorized: 0x1n,
48
+ TrackEnded: 0x2n,
49
+ SubscriptionEnded: 0x3n,
50
+ GoingAway: 0x4n,
51
+ Expired: 0x5n,
52
+ TooFarBehind: 0x6n,
53
+ MalformedTrack: 0x7n,
54
+ UpdateFailed: 0x8n,
55
+ } as const
56
+ export type PublishDoneStatusCodeValue =
57
+ (typeof PublishDoneStatusCode)[keyof typeof PublishDoneStatusCode]
58
+
59
+ export const DataStreamResetErrorCode = {
60
+ InternalError: 0x0n,
61
+ Cancelled: 0x1n,
62
+ DeliveryTimeout: 0x2n,
63
+ SessionClosed: 0x3n,
64
+ } as const
65
+ export type DataStreamResetErrorCodeValue =
66
+ (typeof DataStreamResetErrorCode)[keyof typeof DataStreamResetErrorCode]
@@ -1,5 +1,7 @@
1
1
  export const DRAFT_VERSION = 0xff00000fn
2
2
 
3
+ export type { AuthRedaction } from '../../core/auth-redaction.js'
4
+ export { REDACTION_FILL } from '../../core/auth-redaction.js'
3
5
  export type { Draft15Codec } from './codec.js'
4
6
  export {
5
7
  createDataStreamDecoder,
@@ -16,7 +18,20 @@ export {
16
18
  encodeFetchStream,
17
19
  encodeMessage,
18
20
  encodeSubgroupStream,
21
+ redactAuthTokens,
19
22
  } from './codec.js'
23
+ export type {
24
+ DataStreamResetErrorCodeValue,
25
+ PublishDoneStatusCodeValue,
26
+ RequestErrorCodeValue,
27
+ SessionErrorCodeValue,
28
+ } from './error-codes.js'
29
+ export {
30
+ DataStreamResetErrorCode,
31
+ PublishDoneStatusCode,
32
+ RequestErrorCode,
33
+ SessionErrorCode,
34
+ } from './error-codes.js'
20
35
  export {
21
36
  MESSAGE_ID_MAP,
22
37
  MESSAGE_TYPE_MAP,
@@ -342,10 +342,17 @@ export type Draft15DataStream = SubgroupStream | DatagramObject | FetchStream
342
342
  // Streaming data stream decoder types
343
343
  export interface SubgroupStreamHeader {
344
344
  readonly type: 'subgroup_header'
345
+ /**
346
+ * The stream Type Flags, the same value `SubgroupStream.headerType` carries.
347
+ * Without it the incremental decoder discarded every flag the header
348
+ * encodes and a consumer could not tell the modes apart.
349
+ */
350
+ readonly headerType: number
345
351
  readonly trackAlias: bigint
346
352
  readonly groupId: bigint
347
353
  readonly subgroupId: bigint
348
354
  readonly publisherPriority: number
355
+ readonly endOfGroup?: boolean
349
356
  }
350
357
 
351
358
  export interface FetchStreamHeader {
@@ -1,3 +1,4 @@
1
+ import { type AuthRedaction, recordAuthValueSpan, redactWith } from '../../core/auth-redaction.js'
1
2
  import { BufferReader } from '../../core/buffer-reader.js'
2
3
  import { BufferWriter } from '../../core/buffer-writer.js'
3
4
  import { bytesToHex, hexToBytes } from '../../core/hex.js'
@@ -62,89 +63,126 @@ const textDecoder = /* @__PURE__ */ new TextDecoder()
62
63
  // ─── Setup Parameter Encoding/Decoding ──────────────────────────────────────────
63
64
 
64
65
  function encodeSetupParams(params: Draft16SetupParams, writer: BufferWriter): void {
65
- let count = 0
66
- if (params.path !== undefined) count++
67
- if (params.max_request_id !== undefined) count++
68
- count += params.authorization_token?.length ?? 0
69
- if (params.max_auth_token_cache_size !== undefined) count++
70
- if (params.authority !== undefined) count++
71
- if (params.moqt_implementation !== undefined) count++
72
- if (params.unknown) count += params.unknown.length
73
-
74
- writer.writeVarInt(count)
66
+ // Draft-16 Section 9.2: "Parameters are serialized as Key-Value-Pairs." That
67
+ // includes Setup Parameters, so Section 1.4.2 applies here exactly as it does
68
+ // in encodeParams: "Key-Value-Pairs encode a Type value as a delta from the
69
+ // previous Type value, or from 0 if there is no previous Type value." A Delta
70
+ // Type is an unsigned varint, so ascending order is the only order the wire
71
+ // format can express. The entries are collected, sorted, and written as
72
+ // differences. Drafts 15 and earlier wrote the Type absolutely.
73
+ const entries: Array<{ type: bigint; encode: (w: BufferWriter) => void }> = []
75
74
 
76
75
  // PATH (0x01) - odd, length-prefixed bytes
77
76
  if (params.path !== undefined) {
78
- writer.writeVarInt(SETUP_PARAM_PATH)
79
- const encoded = textEncoder.encode(params.path)
80
- writer.writeVarInt(encoded.byteLength)
81
- writer.writeBytes(encoded)
77
+ entries.push({
78
+ type: SETUP_PARAM_PATH,
79
+ encode: (w) => {
80
+ const encoded = textEncoder.encode(params.path!)
81
+ w.writeVarInt(encoded.byteLength)
82
+ w.writeBytes(encoded)
83
+ },
84
+ })
82
85
  }
83
86
 
84
87
  // MAX_REQUEST_ID (0x02) - even, varint value
85
88
  if (params.max_request_id !== undefined) {
86
- writer.writeVarInt(SETUP_PARAM_MAX_REQUEST_ID)
87
- writer.writeVarInt(params.max_request_id)
89
+ entries.push({
90
+ type: SETUP_PARAM_MAX_REQUEST_ID,
91
+ encode: (w) => w.writeVarInt(params.max_request_id!),
92
+ })
88
93
  }
89
94
 
90
95
  // AUTHORIZATION_TOKEN (0x03) - odd, length-prefixed with nested structure
91
96
  for (const token of params.authorization_token ?? []) {
92
- writer.writeVarInt(SETUP_PARAM_AUTHORIZATION_TOKEN)
93
- const tmpW = new BufferWriter(64)
94
- encodeAuthorizationToken(token, tmpW)
95
- const raw = tmpW.finish()
96
- writer.writeVarInt(raw.byteLength)
97
- writer.writeBytes(raw)
97
+ entries.push({
98
+ type: SETUP_PARAM_AUTHORIZATION_TOKEN,
99
+ encode: (w) => {
100
+ const tmpW = new BufferWriter(64)
101
+ encodeAuthorizationToken(token, tmpW)
102
+ const raw = tmpW.finish()
103
+ w.writeVarInt(raw.byteLength)
104
+ w.writeBytes(raw)
105
+ },
106
+ })
98
107
  }
99
108
 
100
109
  // MAX_AUTH_TOKEN_CACHE_SIZE (0x04) - even, varint value
101
110
  if (params.max_auth_token_cache_size !== undefined) {
102
- writer.writeVarInt(SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE)
103
- writer.writeVarInt(params.max_auth_token_cache_size)
111
+ entries.push({
112
+ type: SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE,
113
+ encode: (w) => w.writeVarInt(params.max_auth_token_cache_size!),
114
+ })
104
115
  }
105
116
 
106
117
  // AUTHORITY (0x05) - odd, length-prefixed bytes
107
118
  if (params.authority !== undefined) {
108
- writer.writeVarInt(SETUP_PARAM_AUTHORITY)
109
- const encoded = textEncoder.encode(params.authority)
110
- writer.writeVarInt(encoded.byteLength)
111
- writer.writeBytes(encoded)
119
+ entries.push({
120
+ type: SETUP_PARAM_AUTHORITY,
121
+ encode: (w) => {
122
+ const encoded = textEncoder.encode(params.authority!)
123
+ w.writeVarInt(encoded.byteLength)
124
+ w.writeBytes(encoded)
125
+ },
126
+ })
112
127
  }
113
128
 
114
129
  // MOQT_IMPLEMENTATION (0x07) - odd, length-prefixed bytes
115
130
  if (params.moqt_implementation !== undefined) {
116
- writer.writeVarInt(SETUP_PARAM_MOQT_IMPLEMENTATION)
117
- const encoded = textEncoder.encode(params.moqt_implementation)
118
- writer.writeVarInt(encoded.byteLength)
119
- writer.writeBytes(encoded)
131
+ entries.push({
132
+ type: SETUP_PARAM_MOQT_IMPLEMENTATION,
133
+ encode: (w) => {
134
+ const encoded = textEncoder.encode(params.moqt_implementation!)
135
+ w.writeVarInt(encoded.byteLength)
136
+ w.writeBytes(encoded)
137
+ },
138
+ })
120
139
  }
121
140
 
122
141
  // Unknown params
123
142
  if (params.unknown) {
124
143
  for (const u of params.unknown) {
125
144
  const id = BigInt(u.id)
126
- writer.writeVarInt(id)
127
- if (id % 2n === 0n) {
128
- const raw = hexToBytes(u.raw_hex)
129
- const tmpReader = new BufferReader(raw)
130
- const value = tmpReader.readVarInt()
131
- writer.writeVarInt(value)
132
- } else {
133
- const raw = hexToBytes(u.raw_hex)
134
- writer.writeVarInt(raw.byteLength)
135
- writer.writeBytes(raw)
136
- }
145
+ entries.push({
146
+ type: id,
147
+ encode: (w) => {
148
+ const raw = hexToBytes(u.raw_hex)
149
+ if (id % 2n === 0n) {
150
+ w.writeVarInt(new BufferReader(raw).readVarInt())
151
+ } else {
152
+ w.writeVarInt(raw.byteLength)
153
+ w.writeBytes(raw)
154
+ }
155
+ },
156
+ })
137
157
  }
138
158
  }
159
+
160
+ // Stable, so a repeated AUTHORIZATION_TOKEN keeps the order it was given in
161
+ // and encodes as a Delta Type of 0.
162
+ entries.sort((a, b) => (a.type < b.type ? -1 : a.type > b.type ? 1 : 0))
163
+
164
+ writer.writeVarInt(entries.length)
165
+ let prevType = 0n
166
+ for (const entry of entries) {
167
+ writer.writeVarInt(entry.type - prevType)
168
+ entry.encode(writer)
169
+ prevType = entry.type
170
+ }
139
171
  }
140
172
 
141
173
  function decodeSetupParams(reader: BufferReader): Draft16SetupParams {
142
174
  const count = Number(reader.readVarInt())
143
175
  const result: Draft16SetupParams = {}
144
176
  const unknown: UnknownParam[] = []
177
+ let prevType = 0n
145
178
 
146
179
  for (let i = 0; i < count; i++) {
147
- const paramType = reader.readVarInt()
180
+ // Draft-16 Section 1.4.2: the Type is a delta from the previous Type, or
181
+ // from 0 for the first pair. Parity -- "Length: Only present when Type is
182
+ // odd" -- is decided by the resolved Type, never by the delta.
183
+ const delta = reader.readVarInt()
184
+ const paramType = prevType + delta
185
+ prevType = paramType
148
186
 
149
187
  if (paramType % 2n === 0n) {
150
188
  const value = reader.readVarInt()
@@ -168,10 +206,9 @@ function decodeSetupParams(reader: BufferReader): Draft16SetupParams {
168
206
  if (paramType === SETUP_PARAM_PATH) {
169
207
  result.path = textDecoder.decode(bytes)
170
208
  } else if (paramType === SETUP_PARAM_AUTHORIZATION_TOKEN) {
171
- result.authorization_token = [
172
- ...(result.authorization_token ?? []),
173
- decodeAuthorizationToken(new BufferReader(bytes)),
174
- ]
209
+ const token = decodeAuthorizationToken(new BufferReader(bytes))
210
+ noteAuthTokenValue(reader.offset - length, length, token.token_value)
211
+ result.authorization_token = [...(result.authorization_token ?? []), token]
175
212
  } else if (paramType === SETUP_PARAM_AUTHORITY) {
176
213
  result.authority = textDecoder.decode(bytes)
177
214
  } else if (paramType === SETUP_PARAM_MOQT_IMPLEMENTATION) {
@@ -460,10 +497,9 @@ function decodeParams(reader: BufferReader): Draft16Params {
460
497
  } else if (paramType === PARAM_AUTHORIZATION_TOKEN) {
461
498
  const length = Number(reader.readVarInt())
462
499
  const tokenBytes = reader.readBytes(length)
463
- result.authorization_token = [
464
- ...(result.authorization_token ?? []),
465
- decodeAuthorizationToken(new BufferReader(tokenBytes)),
466
- ]
500
+ const token = decodeAuthorizationToken(new BufferReader(tokenBytes))
501
+ noteAuthTokenValue(reader.offset - length, length, token.token_value)
502
+ result.authorization_token = [...(result.authorization_token ?? []), token]
467
503
  } else if (paramType === PARAM_MAX_CACHE_DURATION) {
468
504
  result.max_cache_duration = reader.readVarInt()
469
505
  } else if (paramType === PARAM_EXPIRES) {
@@ -1268,3 +1304,61 @@ export function createDraft16Codec(): Draft16Codec {
1268
1304
  createDataStreamDecoder,
1269
1305
  }
1270
1306
  }
1307
+
1308
+ /**
1309
+ * Record where a Token Value sits, so it can be overwritten before the frame
1310
+ * reaches anything that keeps it.
1311
+ *
1312
+ * The span falls out of the decode with no second walk. The Token Value is the
1313
+ * last field of the Token structure, so its start is the parameter value's end
1314
+ * minus the value's own length. The fields before it are left alone
1315
+ * deliberately: Alias Type, Token Alias and Token Type are structure rather
1316
+ * than secret, and they are what distinguishes a session that failed to
1317
+ * authenticate from one that never tried.
1318
+ *
1319
+ * A token carrying no value at all -- DELETE and USE_ALIAS -- records nothing,
1320
+ * and its frame passes through untouched.
1321
+ */
1322
+ function noteAuthTokenValue(
1323
+ valueStart: number,
1324
+ valueLength: number,
1325
+ value: Uint8Array | undefined,
1326
+ ): void {
1327
+ if (value === undefined || value.byteLength === 0) return
1328
+ recordAuthValueSpan(valueStart + valueLength - value.byteLength, value.byteLength)
1329
+ }
1330
+
1331
+ /**
1332
+ * Overwrite every Authorization Token value in a draft-16 control frame.
1333
+ *
1334
+ * One decode, whose *output is discarded*: the point is not to read the
1335
+ * message, it is to learn where the credentials were so the caller never has to
1336
+ * hold a frame that contains one. A caller that also wants the message should
1337
+ * decode the **redacted** frame with {@link decodeMessage}, which then cannot
1338
+ * produce a value because there is no longer one in the bytes.
1339
+ *
1340
+ * A frame that fails to decode is still redacted as far as the decode got. That
1341
+ * is the useful direction to fail in: a value that was read is a value that was
1342
+ * exposed, whatever went wrong after it.
1343
+ *
1344
+ * See {@link AuthRedaction.incomplete} for the one result a caller must not
1345
+ * treat as clean, and {@link AuthRedaction.decoded} for the residual it cannot
1346
+ * close.
1347
+ */
1348
+ export function redactAuthTokens(bytes: Uint8Array): AuthRedaction {
1349
+ let payloadStart: number
1350
+ try {
1351
+ const probe = new BufferReader(bytes)
1352
+ probe.readVarInt()
1353
+ probe.readUint8()
1354
+ probe.readUint8()
1355
+ payloadStart = probe.offset
1356
+ } catch {
1357
+ // Too short to carry a message header, and so too short for the parameters
1358
+ // that carry a credential. Reported as not decoded rather than as clean: a
1359
+ // framer only ever emits complete frames, so a caller that gets here is
1360
+ // holding something this function did not check.
1361
+ return { bytes, redacted: 0, incomplete: false, decoded: false }
1362
+ }
1363
+ return redactWith(bytes, payloadStart, () => decodeMessage(bytes).ok)
1364
+ }