@blockcast/mmt-container 1.0.1 → 2.0.0-main.609e08d7e29f
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/README.md +9 -7
- package/dist/browser-runtime.js +6972 -6693
- package/dist/browser-runtime.js.map +4 -4
- package/dist/carp-catalog.d.ts +7 -7
- package/dist/carp-catalog.d.ts.map +1 -1
- package/dist/carp-catalog.js +65 -230
- package/dist/carp-catalog.js.map +3 -3
- package/dist/catalog-fec-params.js.map +1 -1
- package/dist/catalog-network-source.js.map +1 -1
- package/dist/delivery-catalog.js +3 -0
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/delta-fallback-config.js.map +1 -1
- package/dist/fec-manager.d.ts +25 -1
- package/dist/fec-manager.d.ts.map +1 -1
- package/dist/fec-manager.js +208 -15
- package/dist/fec-manager.js.map +2 -2
- package/dist/fec-state.js.map +1 -1
- package/dist/fec-worker-setup.js.map +1 -1
- package/dist/held-layer-admission.d.ts +8 -0
- package/dist/held-layer-admission.d.ts.map +1 -1
- package/dist/held-layer-admission.js +22 -1
- package/dist/held-layer-admission.js.map +2 -2
- package/dist/iso-block-locator.js.map +1 -1
- package/dist/mfu-reassembler.js.map +1 -1
- package/dist/mmtp-container-parser.js.map +1 -1
- package/dist/mmtp-container-parser.no-modules.js +146 -332
- package/dist/mmtp-container-parser.no-modules.js.map +4 -4
- package/dist/mmtp-decode-pipeline.js.map +1 -1
- package/dist/mmtp-fec-session.d.ts.map +1 -1
- package/dist/mmtp-fec-session.js +61 -15
- package/dist/mmtp-fec-session.js.map +3 -3
- package/dist/mmtp-router.d.ts +11 -6
- package/dist/mmtp-router.d.ts.map +1 -1
- package/dist/mmtp-router.js +47 -10
- package/dist/mmtp-router.js.map +3 -3
- package/dist/multicast-auth-catalog.d.ts +8 -8
- package/dist/multicast-auth-catalog.d.ts.map +1 -1
- package/dist/multicast-auth-catalog.js +171 -75
- package/dist/multicast-auth-catalog.js.map +4 -4
- package/dist/multicast-input.d.ts +5 -5
- package/dist/multicast-input.d.ts.map +1 -1
- package/dist/multicast-input.js +126 -49
- package/dist/multicast-input.js.map +3 -3
- package/dist/multicast-manager.d.ts +11 -4
- package/dist/multicast-manager.d.ts.map +1 -1
- package/dist/multicast-manager.js +7914 -7659
- package/dist/multicast-manager.js.map +4 -4
- package/dist/multicast-transport.d.ts +16 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +120 -5
- package/dist/multicast-transport.js.map +4 -4
- package/dist/multicast-unwrap.js +2 -2
- package/dist/multicast-unwrap.js.map +2 -2
- package/dist/no-modules.js +100 -288
- package/dist/no-modules.js.map +4 -4
- package/dist/repair-fetch.js.map +1 -1
- package/dist/signaling.js.map +1 -1
- package/dist/timestamp-codec.js.map +1 -1
- package/dist/wasm-mfu-reassembler.js.map +1 -1
- package/dist/wasm-mmtp-container-parser.js.map +1 -1
- package/package.json +5 -5
- package/schemas/catalog.schema.json +15 -7
- package/src/carp-catalog.ts +14 -13
- package/src/fec-manager.ts +89 -14
- package/src/held-layer-admission.ts +38 -1
- package/src/mmtp-fec-session.ts +20 -12
- package/src/mmtp-router.ts +65 -16
- package/src/multicast-auth-catalog.ts +43 -36
- package/src/multicast-input.ts +28 -8
- package/src/multicast-manager.ts +38 -34
- package/src/multicast-transport.ts +74 -7
- package/test-vectors/bc-provenance-v2.json +614 -175
- package/test-vectors/catalog/README.md +24 -8
- package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
- package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
- package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
- package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
- package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
- package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
- package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
- package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
- package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
- package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
- package/test-vectors/catalog/section-codes.json +8 -3
- package/test-vectors/catalog-multicast-auth-v1.json +0 -1
- package/test-vectors/catalog-validation.json +3 -3
- package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../mmt-base/src/mmtp.ts", "../../mmt-base/src/ntp-short.ts", "../../mmt-base/src/fec-block-profile.ts", "../../mmt-base/src/multicast-catalog.ts", "../../mmt-base/src/fec-bindings.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/utils.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/_md.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/sha2.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/sha256.ts", "../../mmt-manifest-verify/src/bc-provenance-v2.ts", "../src/multicast-auth-catalog.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @mmt/container - MMTP Parser\n *\n * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2\n *\n * MMTP header format (12 bytes):\n * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1)\n * - Byte 1: reserved(2) | packet_type(6)\n * - Bytes 2-3: packet_id (big-endian)\n * - Bytes 4-7: timestamp (big-endian, 32-bit)\n * - Bytes 8-11: sequence_number (big-endian)\n *\n * FEC_type values:\n * - 0: No FEC / source packet\n * - 1: FEC mode 0 (source with FEC payload ID)\n * - 2: FEC repair packet mode 0\n * - 3: FEC repair packet mode 1\n *\n * @packageDocumentation\n */\n\nexport interface MMTPHeader {\n version: number\n /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */\n packetType: number\n packetId: number\n timestamp: bigint\n sequenceNumber: number\n payloadLength: number\n fecType: number\n /** C flag: header carries a 32-bit packet_counter. */\n packetCounterFlag: boolean\n packetCounter?: number\n /** X flag: header carries an extension header and value. */\n extensionFlag: boolean\n extension?: { type: number; data: Uint8Array }\n /** Byte offset at which the MMTP payload begins. */\n headerLength: number\n /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */\n rapFlag: boolean\n}\n\nexport interface MPUHeader {\n mpuSequenceNumber: number\n fragmentationIndicator: number // FI: 0=complete, 1=first, 2=middle, 3=last\n /** 8-bit MPU fragment_counter from byte 3. */\n fragmentCounter: number\n /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */\n timedFlag: number\n /** A flag: aggregated MPU payload. This strict profile requires A=0. */\n aggregationFlag: boolean\n /** MPU payload_length (bytes 0-1): bytes after the length field, including\n * the remaining six MPU-header bytes and all data-unit bytes. */\n payloadLength: number\n}\n\nexport interface MFUDuHeader {\n movieFragmentSequenceNumber: number\n sampleNumber: number\n offset: number\n priority: number\n dependencyCounter: number\n}\n\nexport interface MMTPPacket {\n header: MMTPHeader\n payload: Uint8Array\n}\n\nexport const MMTP_HEADER_SIZE = 12\nexport const MPU_HEADER_SIZE = 8\nexport const MFU_DU_HEADER_SIZE_TIMED = 14 // T=1: timed media (video/audio samples)\nexport const MFU_DU_HEADER_SIZE_NONTIMED = 4 // T=0: non-timed media (item_id only)\n\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nexport const SOURCE_FEC_PAYLOAD_ID_SIZE = 4\n\n// MMTP Packet Types (ISO/IEC 23008-1:2023 Section 9.2.2)\nexport const PACKET_TYPE_MPU = 0x00\nexport const PACKET_TYPE_SIGNALING = 0x02\nexport const PACKET_TYPE_REPAIR = 0x03\n\n// AL-FEC Signaling Message ID (ISO/IEC 23008-1:2023 Amendment 1:2025)\nexport const ALFEC_MESSAGE_ID = 0x0203\n\n/**\n * MMTP `packet_id` of the signaling flow, which carries PACKET_TYPE_SIGNALING\n * packets such as the AL-FEC message (ALFEC_MESSAGE_ID). The FFmpeg `moq_mmt`\n * muxer sends that message on this packet_id into the same multicast group as\n * the media, and moqcast-draft (PR #44) makes advertising it for a track a\n * MUST NOT. A track's packet_id is therefore 1..MMTP_PACKET_ID_MAX.\n */\nexport const MMTP_SIGNALING_PACKET_ID = 0\n\n/** Largest value of the 16-bit MMTP `packet_id` header field. */\nexport const MMTP_PACKET_ID_MAX = 0xffff\n\n// fec_code_id_for_repair_flow code points (ISO/IEC 23008-10 Table 1;\n// draft-ramadan-moq-mmt-01 Section 8.3).\nexport const FEC_CODE_REED_SOLOMON = 1\nexport const FEC_CODE_RAPTORQ = 3\n\n/**\n * Parse MMTP header from buffer\n */\nexport function parseMMTPHeader(buffer: Uint8Array): MMTPHeader {\n if (buffer.length < MMTP_HEADER_SIZE) {\n throw new Error(`MMTP packet too small: ${buffer.length} bytes`)\n }\n\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n\n const version = (buffer[0]! >> 6) & 0x03\n if (version !== 0) {\n throw new Error(`Unsupported MMTP version: ${version}`)\n }\n\n // Extract FEC type from byte 0, bits 3-4\n const fecType = (buffer[0]! >> 3) & 0x03\n const packetCounterFlag = (buffer[0]! & 0x20) !== 0\n const extensionFlag = (buffer[0]! & 0x02) !== 0\n\n // Extract RAP flag from byte 0, bit 0 (R bit - Random Access Point)\n const rapFlag = (buffer[0]! & 0x01) === 1\n\n // ISO/IEC 23008-1:2023 Figure 8 (V=0) places reserved(2) before\n // packet_type(6), so packet_type occupies the low six bits of byte 1.\n const packetType = buffer[1]! & 0x3F\n\n let offset = 2\n\n // Packet ID (2 bytes, big-endian)\n const packetId = view.getUint16(offset, false)\n offset += 2\n\n // Timestamp (4 bytes, big-endian)\n const timestamp = BigInt(view.getUint32(offset, false))\n offset += 4\n\n // Sequence number (4 bytes, big-endian)\n const sequenceNumber = view.getUint32(offset, false)\n offset += 4\n\n let packetCounter: number | undefined\n if (packetCounterFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in packet_counter')\n packetCounter = view.getUint32(offset, false)\n offset += 4\n }\n\n let extension: { type: number; data: Uint8Array } | undefined\n if (extensionFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in header extension')\n const type = view.getUint16(offset, false)\n const length = view.getUint16(offset + 2, false)\n offset += 4\n if (offset + length > buffer.length) throw new Error('MMTP packet truncated in header extension value')\n extension = { type, data: buffer.subarray(offset, offset + length) }\n offset += length\n }\n\n return {\n version,\n packetType,\n packetId,\n timestamp,\n sequenceNumber,\n payloadLength: buffer.length - offset,\n fecType,\n packetCounterFlag,\n packetCounter,\n extensionFlag,\n extension,\n headerLength: offset,\n rapFlag,\n }\n}\n\n/**\n * The MMTP `packet_id` of `buffer`, or null exactly when parseMMTPHeader would\n * throw for it (short buffer, V != 0, truncated packet_counter or header\n * extension).\n *\n * This is the per-packet filter a receiver runs before any other work, so it\n * builds no header object, DataView or BigInt timestamp (BLO-36599). Use\n * parseMMTPHeader when any other header field is needed.\n */\nexport function readMMTPPacketId(buffer: Uint8Array): number | null {\n if (buffer.length < MMTP_HEADER_SIZE) return null\n const flags = buffer[0]!\n if (((flags >> 6) & 0x03) !== 0) return null\n let offset = MMTP_HEADER_SIZE\n if ((flags & 0x20) !== 0) {\n if (offset + 4 > buffer.length) return null\n offset += 4\n }\n if ((flags & 0x02) !== 0) {\n if (offset + 4 > buffer.length) return null\n const length = (buffer[offset + 2]! << 8) | buffer[offset + 3]!\n if (offset + 4 + length > buffer.length) return null\n }\n return (buffer[2]! << 8) | buffer[3]!\n}\n\n/**\n * Unwrap MMTP packet to extract payload\n */\nexport function unwrapMMTP(buffer: Uint8Array): MMTPPacket {\n const header = parseMMTPHeader(buffer)\n const payload = buffer.subarray(header.headerLength)\n\n return { header, payload }\n}\n\n/**\n * Parse MPU header from payload\n * ISO/IEC 23008-1:2023 Section 9.2.3.3\n *\n * MPU Header format (8 bytes) as written by FFmpeg/libmmt:\n * - Bytes 0-1: payload_length (16 bits, big-endian)\n * - Byte 2: FT(4) | T(1) | FI(2) | A(1)\n * - Byte 3: fragment_counter (8 bits)\n * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n */\nexport function parseMPUHeader(payload: Uint8Array): MPUHeader {\n if (payload.length < MPU_HEADER_SIZE) {\n throw new Error(`Payload too small for MPU header: ${payload.length} bytes`)\n }\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)\n\n // Bytes 0-1: payload_length (16 bits, big-endian)\n const payloadLength = view.getUint16(0, false)\n\n // Byte 2: FT(4) | T(1) | FI(2) | A(1)\n const flagsByte = payload[2]!\n const timedFlag = (flagsByte >> 3) & 0x01\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n const aggregationFlag = (flagsByte & 0x01) !== 0\n\n // Byte 3: fragment_counter (8 bits). The high bit is part of the counter in\n // the current libmmt/FFmpeg writer ABI.\n const fragmentCounter = payload[3]!\n\n // Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n const mpuSequenceNumber = view.getUint32(4, false)\n\n return {\n mpuSequenceNumber,\n fragmentationIndicator,\n fragmentCounter,\n timedFlag,\n aggregationFlag,\n payloadLength,\n }\n}\n\nexport function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null {\n if (offset < 0 || offset + MFU_DU_HEADER_SIZE_TIMED > payload.length) return null\n const view = new DataView(payload.buffer, payload.byteOffset + offset, MFU_DU_HEADER_SIZE_TIMED)\n return {\n movieFragmentSequenceNumber: view.getUint32(0, false),\n sampleNumber: view.getUint32(4, false),\n offset: view.getUint32(8, false),\n priority: payload[offset + 12]!,\n dependencyCounter: payload[offset + 13]!,\n }\n}\n\n/**\n * Why `extractMFUPayload` refused a packet.\n *\n * These identifiers are part of the public contract: callers surface them in\n * diagnostics, so renaming one is a breaking change.\n */\nexport type MFURejectionGate =\n | 'packet-shorter-than-mmtp-plus-mpu-header'\n | 'mmtp-header-unparseable'\n | 'not-an-mpu-packet'\n | 'payload-shorter-than-mpu-header'\n | 'aggregation-flag-set'\n | 'mpu-payload-length-below-header-remainder'\n | 'mpu-declared-end-exceeds-payload'\n | 'mfu-offset-at-or-past-declared-end'\n | 'source-fec-payload-id-width-mismatch'\n | 'declared-trailers-unresolved'\n\nexport interface MFURejection {\n gate: MFURejectionGate\n /** Human-readable detail, including the byte arithmetic that decided it. */\n detail: string\n}\n\ninterface MfuPayloadLayout {\n hasMfuDuHeader: boolean\n mfuOffset: number\n dataEnd: number\n}\n\nfunction resolveMfuPayloadLayout(\n mpu: MPUHeader,\n fragmentType: number,\n payload: Uint8Array,\n): { layout: MfuPayloadLayout } | { rejection: MFURejection } {\n if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {\n return {\n rejection: {\n gate: 'mpu-payload-length-below-header-remainder',\n detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`,\n },\n }\n }\n\n // ISO/IEC 23008-1 defines payload_length from immediately after its own\n // two-byte field. It therefore includes the remaining six MPU-header bytes.\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength\n if (declaredEnd > payload.length) {\n return {\n rejection: {\n gate: 'mpu-declared-end-exceeds-payload',\n detail:\n `payload_length=${mpu.payloadLength} implies end=${declaredEnd}, ` +\n `payload is ${payload.length} bytes`,\n },\n }\n }\n\n const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1\n const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED\n const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0)\n if (mfuOffset >= declaredEnd) {\n return {\n rejection: {\n gate: 'mfu-offset-at-or-past-declared-end',\n detail: `mfuOffset=${mfuOffset} >= declaredEnd=${declaredEnd}`,\n },\n }\n }\n\n return {\n layout: {\n hasMfuDuHeader: hasHeader,\n mfuOffset,\n dataEnd: declaredEnd,\n },\n }\n}\n\n// Legacy detached per-packet authenticator suffix `[0xf0a1][len][auth]`. Its\n// producer was removed (BLO-16483) and nothing verifies it any more; provenance\n// is the signed per-group manifest. Receivers still skip an exact suffix so a\n// stale producer degrades to unauthenticated playback instead of dropped\n// packets. ponytail: hard-cut to reject once no deployed producer can emit it.\nconst LEGACY_TRAILER_EXT_TYPE = 0xf0a1\nconst LEGACY_TRAILER_HEADER_SIZE = 4\n\nfunction isEmptyOrExactLegacyTrailer(payload: Uint8Array, start: number, end: number): boolean {\n if (start === end) return true\n if (start < 0 || end > payload.length || start + LEGACY_TRAILER_HEADER_SIZE > end) {\n return false\n }\n\n const view = new DataView(\n payload.buffer,\n payload.byteOffset + start,\n end - start,\n )\n if (view.getUint16(0, false) !== LEGACY_TRAILER_EXT_TYPE) return false\n\n const authLength = view.getUint16(2, false)\n return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end\n}\n\ninterface DeclaredTrailerLayout {\n hasFecId: boolean\n hasOverlayFecId: boolean\n}\n\n/** Options for extractMFUPayload and explainMFURejection. */\nexport interface MFUExtractOptions {\n /**\n * When true, do NOT subtract the Source FEC Payload ID from the payload\n * boundary, even if fecType >= 1. For FEC-recovered symbols whose FEC PID\n * was already stripped before WASM decoding.\n */\n skipFecId?: boolean\n /**\n * The catalog declares a keyframe overlay (`scope: \"keyframe\"` repair track)\n * for this packet's source. A RAP packet then carries two Source FEC Payload\n * IDs, base first and overlay last (BLO-32719 Clarification 7). This is a\n * catalog fact, never inferred: the header has no overlay bit, and a payload\n * four bytes shorter is byte-identical to a missing overlay. Mirrors\n * mmt-core `split_payload_and_source_fec_trailers(overlay_declared)`.\n */\n overlayDeclared?: boolean\n}\n\nfunction resolveDeclaredTrailers(\n payload: Uint8Array,\n declaredEnd: number,\n fecType: number,\n options?: MFUExtractOptions,\n rapFlag = false,\n): DeclaredTrailerLayout | null {\n if (declaredEnd > payload.length) return null\n\n if (options?.skipFecId) {\n return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)\n ? { hasFecId: false, hasOverlayFecId: false }\n : null\n }\n\n const overlay = options?.overlayDeclared === true && rapFlag\n if (fecType === 1) {\n const fecStart = payload.length - sourceFecTrailerSize(overlay)\n if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {\n return null\n }\n return { hasFecId: true, hasOverlayFecId: overlay }\n }\n // An overlay ID rides behind a base ID the header says is absent.\n if (fecType !== 0 || overlay) return null\n\n if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {\n return { hasFecId: false, hasOverlayFecId: false }\n }\n\n return null\n}\n\nfunction sourceFecTrailerSize(overlay: boolean): number {\n return overlay ? 2 * SOURCE_FEC_PAYLOAD_ID_SIZE : SOURCE_FEC_PAYLOAD_ID_SIZE\n}\n\n/**\n * Extract NAL unit payload from MMTP packet\n */\nexport function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array {\n const extraction = extractMFUPayload(mmtpPacket)\n if (!extraction) throw new Error('Malformed MMTP MPU payload')\n return extraction.data\n}\n\n/** Result of extracting MFU payload from an MMTP packet */\nexport interface MFUExtraction {\n /** MMTP header fields */\n header: MMTPHeader\n /** MPU header fields */\n mpu: MPUHeader\n /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */\n fragmentType: number\n /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */\n data: Uint8Array\n /** Parsed timed MFU DU header when present, otherwise null */\n mfuDuHeader: MFUDuHeader | null\n /** Non-timed MFU Item_ID when the 4-byte DU header is present. */\n itemId: number | null\n /** Source FEC Payload ID if present (`FEC_type == 1`), or null */\n fecPayloadId: { ssId: number } | null\n /** Keyframe-overlay Source FEC Payload ID: RAP packets with `overlayDeclared` only. */\n overlayFecPayloadId: { ssId: number } | null\n}\n\n/**\n * Extract MFU payload from a raw MMTP packet.\n *\n * Handles all the byte arithmetic for:\n * - MMTP header (12 bytes)\n * - MPU sub-header (8 bytes, payloadLength field)\n * - MFU DU header (14 bytes timed / 4 bytes non-timed, only for FI \u2264 1)\n * - Source FEC Payload ID (4 bytes at end, only when `FEC_type == 1`)\n *\n * `payloadLength` is the ISO length-after-field value: six remaining MPU-header\n * bytes, then the MFU DU header when present, then media. The legacy\n * authenticator suffix and the Source FEC trailer are outside that declared span and must consume the exact suffix.\n *\n * @param mmtpPacket Raw MMTP packet (from multicast or MoQ)\n * @param options.skipFecId When true, do NOT subtract the 4-byte Source FEC Payload ID\n * from the payload boundary, even if fecType >= 1 in the MMTP header. Use this for\n * FEC-recovered symbols where the FEC PID was already stripped before WASM decoding\n * but the fecType bits in byte 0 still reflect the original packet (FEC Type=1 per\n * draft-ramadan-moq-mmt-01 \u00A73.1).\n * @param options.overlayDeclared The catalog declares a keyframe overlay for this\n * source, so a RAP packet carries an 8-byte trailer (see MFUExtractOptions).\n * @returns Extracted MFU data + metadata, or null if packet is too small\n */\ninterface MfuPlan {\n header: MMTPHeader\n payload: Uint8Array\n mpu: MPUHeader\n fragmentType: number\n layout: MfuPayloadLayout\n trailerLayout: DeclaredTrailerLayout\n}\n\n/**\n * Single source of truth for whether a packet yields an MFU payload.\n *\n * `extractMFUPayload` and `explainMFURejection` both delegate here, so the\n * reason reported to operators can never drift from the decision the parser\n * actually made.\n */\nfunction planMfuExtraction(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n parsedHeader?: MMTPHeader,\n): { plan: MfuPlan } | { rejection: MFURejection } {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'packet-shorter-than-mmtp-plus-mpu-header',\n detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`,\n },\n }\n }\n\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch (error) {\n return {\n rejection: {\n gate: 'mmtp-header-unparseable',\n detail: error instanceof Error ? error.message : String(error),\n },\n }\n }\n }\n if (header.packetType !== PACKET_TYPE_MPU) {\n return {\n rejection: {\n gate: 'not-an-mpu-packet',\n detail: `packetType=0x${header.packetType.toString(16).padStart(2, '0')}`,\n },\n }\n }\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'payload-shorter-than-mpu-header',\n detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`,\n },\n }\n }\n\n const mpu = parseMPUHeader(payload)\n if (mpu.aggregationFlag) {\n return {\n rejection: { gate: 'aggregation-flag-set', detail: 'strict profile requires A=0' },\n }\n }\n\n // Fragment type from byte 2 upper nibble (not returned by parseMPUHeader)\n const fragmentType = (payload[2]! >> 4) & 0x0f\n\n const layoutResult = resolveMfuPayloadLayout(mpu, fragmentType, payload)\n if ('rejection' in layoutResult) return layoutResult\n const { layout } = layoutResult\n\n const trailerLayout = resolveDeclaredTrailers(\n payload,\n layout.dataEnd,\n header.fecType,\n options,\n header.rapFlag,\n )\n if (!trailerLayout) {\n // The most common cause is a producer whose Source FEC Payload ID width\n // disagrees with ISO/IEC 23008-1 \u00A7C.5.2. Name that explicitly rather than\n // leaving operators to infer it from a generic trailer error.\n if (!options?.skipFecId && header.fecType === 1) {\n const observed = payload.length - layout.dataEnd\n const expected = sourceFecTrailerSize(options?.overlayDeclared === true && header.rapFlag)\n if (observed !== expected) {\n return {\n rejection: {\n gate: 'source-fec-payload-id-width-mismatch',\n detail:\n `producer wrote a ${observed}-byte Source FEC Payload ID, ` +\n `ISO/IEC 23008-1 \u00A7C.5.2 requires ${expected} ` +\n `(payload=${payload.length}, declaredEnd=${layout.dataEnd})`,\n },\n }\n }\n }\n return {\n rejection: {\n gate: 'declared-trailers-unresolved',\n detail:\n `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty ` +\n `nor an exact legacy authenticator trailer (fecType=${header.fecType})`,\n },\n }\n }\n\n return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } }\n}\n\n/**\n * Report why `extractMFUPayload` would refuse this packet, or null if it would\n * accept it.\n *\n * `extractMFUPayload` returns bare `null` on ten distinct conditions, which\n * makes a producer/consumer framing disagreement indistinguishable from a\n * publisher that never started \u2014 both render as silence. Use this wherever\n * that distinction matters: diagnostics, contract tests, and operator-facing\n * logs.\n */\nexport function explainMFURejection(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n): MFURejection | null {\n const result = planMfuExtraction(mmtpPacket, options)\n return 'rejection' in result ? result.rejection : null\n}\n\nexport function extractMFUPayload(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options))\n}\n\n/**\n * extractMFUPayload for a caller that already holds this packet's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet; the\n * result is then identical to `extractMFUPayload(mmtpPacket, options)`.\n */\nexport function extractMFUPayloadWithHeader(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n options?: MFUExtractOptions,\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options, header))\n}\n\nfunction extractPlannedMfu(\n mmtpPacket: Uint8Array,\n result: { plan: MfuPlan } | { rejection: MFURejection },\n): MFUExtraction | null {\n if ('rejection' in result) return null\n const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan\n\n const mfuDuHeaderPresent = layout.hasMfuDuHeader\n const mfuOffset = layout.mfuOffset\n const dataEnd = layout.dataEnd\n\n const mfuDuHeader =\n mfuDuHeaderPresent && mpu.timedFlag === 1\n ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE)\n : null\n const itemId =\n mfuDuHeaderPresent && mpu.timedFlag === 0\n ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false)\n : null\n\n const data = payload.subarray(mfuOffset, dataEnd)\n\n const trailerStart =\n mmtpPacket.length - sourceFecTrailerSize(trailerLayout.hasOverlayFecId)\n const ssIdAt = (offset: number) => ({\n ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + offset, 4).getUint32(0, false),\n })\n const fecPayloadId = trailerLayout.hasFecId ? ssIdAt(trailerStart) : null\n const overlayFecPayloadId = trailerLayout.hasOverlayFecId\n ? ssIdAt(trailerStart + SOURCE_FEC_PAYLOAD_ID_SIZE)\n : null\n\n return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId, overlayFecPayloadId }\n}\n\n/**\n * Extract timestamp from MMTP packet\n */\nexport function extractTimestamp(mmtpPacket: Uint8Array): bigint {\n const { header } = unwrapMMTP(mmtpPacket)\n return header.timestamp\n}\n\n/**\n * Check if MMTP packet is an FEC repair packet\n * FEC_type 2 and 3 are repair packets\n */\nexport function isFecRepairPacket(mmtpPacket: Uint8Array): boolean {\n if (mmtpPacket.length < 1) return false\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n return fecType >= 2\n}\n\n/**\n * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP\n * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10.\n *\n * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet\n * for ssbg_mode0 and ssbg_mode1). Block membership and ESI use the\n * configured K; repair SS_Start identifies the first SS_ID of the block and\n * SSB_length records the authored source-symbol count:\n * SBN = floor(SS_ID / K), ESI = SS_ID % K\n * SS_Start = SBN * K (first SS_ID of the block)\n *\n * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec \u00A76.2),\n * and ATSC A/331 \u00A78.1.2.5 which defers to ISO 23008-1 Annex C.\n */\nexport function extractFecPayloadId(mmtpPacket: Uint8Array): { ssId: number } | null {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header ? sourceFecPayloadIdOf(mmtpPacket, header) : null\n}\n\n/**\n * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU\n * payload contains a four-byte Source FEC Payload ID.\n */\nexport function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header !== null && sourceFecPayloadIdOf(mmtpPacket, header) !== null\n}\n\nfunction parseHeaderForSourceFecId(mmtpPacket: Uint8Array): MMTPHeader | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n try {\n return parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n}\n\n/**\n * The Source FEC Payload ID of a packet whose MMTP header the caller already\n * parsed, or null when the packet carries none: present only when FEC_type=1\n * and the exact suffix after the ISO-declared MPU payload holds the four-byte\n * ID (ISO/IEC 23008-1 \u00A7C.5.2). Same answer as extractFecPayloadId, without a\n * second header parse (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet.\n */\nexport function sourceFecPayloadIdOf(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n): { ssId: number } | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n if (header.packetType !== PACKET_TYPE_MPU) return null\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) return null\n const payloadLength = (payload[0]! << 8) | payload[1]!\n if (payloadLength < MPU_HEADER_REMAINDER_SIZE) return null\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength\n if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId) return null\n\n // Flat 4-byte SS_ID: encoder writes block_id * K + symbol_id\n const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n return {\n ssId:\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0,\n }\n}\n\n/** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */\nexport const REPAIR_FEC_PID_SIZE = 13\n\n/**\n * Parse the 13-byte Repair FEC Payload ID after the complete dynamic MMTP header.\n * `parsedHeader`, when given, must be `parseMMTPHeader(mmtpPacket)` of this\n * same packet; it spares the second header parse (BLO-36599).\n */\nfunction readRepairFecPayloadIdFields(mmtpPacket: Uint8Array, parsedHeader?: MMTPHeader): {\n packetId: number; ssStart: number; rsbLength: number; rsId: number; ssbLength: number; dataOffset: number\n} | null {\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n }\n if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2) return null\n\n const off = header.headerLength\n if (mmtpPacket.length < off + REPAIR_FEC_PID_SIZE) return null\n const ssStart =\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0 // 32 bits\n const rsbLength = (mmtpPacket[off + 4]! << 16) | (mmtpPacket[off + 5]! << 8) | mmtpPacket[off + 6]! // 24 bits\n const rsId = (mmtpPacket[off + 7]! << 16) | (mmtpPacket[off + 8]! << 8) | mmtpPacket[off + 9]! // 24 bits\n const ssbLength = (mmtpPacket[off + 10]! << 16) | (mmtpPacket[off + 11]! << 8) | mmtpPacket[off + 12]! // 24 bits\n return {\n packetId: header.packetId,\n ssStart,\n rsbLength,\n rsId,\n ssbLength,\n dataOffset: off + REPAIR_FEC_PID_SIZE,\n }\n}\n\n/**\n * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet.\n * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS):\n * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data]\n * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet.\n *\n * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03.\n * Pre-migration high-bits bytes (0x0c) are rejected \u2014 no compatibility decode.\n */\nexport function extractRepairFecPayloadId(mmtpPacket: Uint8Array): {\n ssStart: number; rsbLength: number; rsId: number; ssbLength: number\n} | null {\n const fields = readRepairFecPayloadIdFields(mmtpPacket)\n if (!fields) return null\n return {\n ssStart: fields.ssStart,\n rsbLength: fields.rsbLength,\n rsId: fields.rsId,\n ssbLength: fields.ssbLength,\n }\n}\n\n/** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */\nexport interface MoqRepairFrame {\n /** MMTP packetId (1=video, 2=audio) \u2014 for routing to correct FEC decoder */\n packetId: number\n /** SS_Start (32 bits): SS_ID of first source symbol in block */\n ssStart: number\n /** RSB_length (24 bits): number of repair symbols (P) */\n rsbLength: number\n /** RS_ID (24 bits): repair symbol index (0-based) */\n rsId: number\n /** SSB_length (24 bits): authored source-symbol count (1..=configured K) */\n ssbLength: number\n /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */\n symbolData: Uint8Array\n}\n\n/**\n * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format).\n *\n * Wire format per ISO 23008-1 \u00A7C.4.3 + \u00A7C.5.3:\n * MMTP Header: fixed 12 bytes plus C/X optional fields\n * then SS_Start (4 bytes) \u2192 block identifier\n * then RSB_length (3 bytes) \u2192 P\n * then RS_ID (3 bytes) \u2192 ESI = K + RS_ID\n * then SSB_length (3 bytes) \u2192 authored source-symbol count (1..=K)\n * then Repair Symbol Data (T bytes)\n *\n * @param frame - Raw MoQ repair frame bytes\n * @returns Parsed frame or null if invalid\n */\nexport function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame))\n}\n\n/**\n * parseMoqRepairFrame for a caller that already holds this frame's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599). The router's\n * multicast repair branch uses it: one repair packet in every K + P otherwise\n * paid two header parses.\n *\n * `header` must be `parseMMTPHeader(frame)` of this same frame; the result is\n * then identical to `parseMoqRepairFrame(frame)`.\n */\nexport function parseMoqRepairFrameWithHeader(\n frame: Uint8Array,\n header: MMTPHeader,\n): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header))\n}\n\nfunction moqRepairFrameFrom(\n frame: Uint8Array,\n repairId: ReturnType<typeof readRepairFecPayloadIdFields>,\n): MoqRepairFrame | null {\n if (!repairId) return null\n\n const symbolData = frame.subarray(repairId.dataOffset)\n if (\n repairId.rsbLength === 0 ||\n repairId.ssbLength === 0 ||\n repairId.rsId >= repairId.rsbLength ||\n symbolData.length === 0\n ) return null\n\n return {\n packetId: repairId.packetId,\n ssStart: repairId.ssStart,\n rsbLength: repairId.rsbLength,\n rsId: repairId.rsId,\n ssbLength: repairId.ssbLength,\n symbolData,\n }\n}\n\n// The signalling-message parsers (9.3.4.2 payload header, AL-FEC message,\n// PA/MPT) live in ./signalling.ts.\n", "/**\n * NTP short format (RFC 5905 \u00A76): an unsigned 32-bit, 16.16 fixed-point count\n * of seconds. The MMTP header `timestamp` field (ISO/IEC 23008-1 \u00A79.2.2)\n * carries each packet's UTC sending instant in this format, so the value wraps\n * to 0 every 2^16 s (~18.2 h).\n *\n * Plain numeric order on these values breaks at every wrap: a retained\n * `Math.max` stays at the pre-wrap value and every later instant compares as\n * older. Order send times only with {@link compareNtpShort}, which applies\n * RFC 1982 serial-number arithmetic over the field's width.\n */\n\n/** Width in bits of the MMTP `timestamp` field (NTP short). */\nexport const NTP_SHORT_BITS = 32\n\n/** Number of distinct NTP-short values; the field wraps to 0 here. */\nexport const NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS\n\n/**\n * RFC 1982 \u00A73.2's `2^(SERIAL_BITS - 1)`. Two values whose forward distance is\n * below this are ordered; exactly this far apart they are not ordered at all.\n * Two instants must be less than this far apart (2^15 s, ~9.1 h of send time)\n * for the comparison to give their true order.\n */\nexport const NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1)\n\n/**\n * Serial order of one NTP-short value relative to another.\n *\n * `'unordered'` is RFC 1982's undefined case: the values are exactly\n * {@link NTP_SHORT_HALF_RANGE} apart, so neither precedes the other. It is\n * never folded into `'before'` or `'after'`; each caller decides what an\n * unorderable pair means for it.\n */\nexport type NtpShortOrder = 'before' | 'equal' | 'after' | 'unordered'\n\n/** True when `value` is an NTP-short value: an integer in [0, NTP_SHORT_MODULUS). */\nexport function isNtpShort(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value < NTP_SHORT_MODULUS\n}\n\n/**\n * RFC 1982 serial comparison of NTP-short `a` against `b`: `'after'` when `a`\n * is the later instant, `'before'` when it is the earlier one.\n *\n * The answer is the true order only while the two instants are less than\n * {@link NTP_SHORT_HALF_RANGE} apart. Past that a 32-bit field cannot tell\n * later from earlier, and the result names whichever direction is nearer.\n * This is not a total order (it is not transitive across the whole space), so\n * it must never be used to sort.\n *\n * @throws {RangeError} if either operand is not an NTP-short value\n */\nexport function compareNtpShort(a: number, b: number): NtpShortOrder {\n if (!isNtpShort(a) || !isNtpShort(b)) {\n throw new RangeError(\n `NTP-short values must be integers in [0, 2^${NTP_SHORT_BITS}), got ${String(a)} and ${String(b)}`,\n )\n }\n if (a === b) return 'equal'\n // Forward distance from b to a, reduced into [1, NTP_SHORT_MODULUS). Both\n // operands are below 2^32, so every step is exact in a double.\n const forward = (a - b + NTP_SHORT_MODULUS) % NTP_SHORT_MODULUS\n if (forward < NTP_SHORT_HALF_RANGE) return 'after'\n if (forward > NTP_SHORT_HALF_RANGE) return 'before'\n return 'unordered'\n}\n", "/**\n * FEC block profile (BLO-35597, ISO/IEC 23008-1 Annex C).\n *\n * Every catalog `fec` object that declares an FEC instance names its block\n * profile in `fec.blockProfile`. A receiver applies all of a profile's rules\n * or none of them, so the token is the one explicit per-stream discriminator:\n * it is never defaulted and never inferred from the wire.\n *\n * draft-ramadan-moq-fec \u00A75.1 defines exactly one value, and it is the only\n * profile this library implements:\n *\n * - `iso-ssbg1-v1`: ssbg_mode1 (C.3.3): a source symbol is\n * `u16 length || protected packet || zero padding` to T; a block is a\n * K' = SSB_length RaptorQ block (C.5.3) and repair ESI = SSB_length + RS_ID;\n * SS_ID increments by one per FEC source packet and wraps at 2^32 (C.5.2),\n * so a block is located by membership in `[SS_Start, SS_Start + SSB_length)`\n * under RFC 1982 serial arithmetic; the MoQ repair group id is the 62-bit\n * wrap-epoch-extended SS_Start. The AL-FEC coding byte for this profile is\n * 0x14 (one-stage, ssbg_mode1, fec_payload_id_mode 0, Amd 1:2025 Table C.3).\n *\n * A track whose `blockProfile` is absent or names any other token (the\n * retired Blockcast-private `k-padded-v1` included) has no FEC: a receiver\n * consumes its source without FEC and joins none of its repair tracks\n * (draft-ramadan-moq-fec \u00A75.1). `parseFecBlockProfile` throws for such a\n * token; the per-track catalog resolver turns the same rule into a typed\n * per-track \"FEC unavailable\" outcome instead of failing the session.\n */\nimport { serial32Add, serial32Distance, serial32Precedes } from './serial-number.js'\n\n/** The block profile a catalog `fec.blockProfile` may name: the one this library implements. */\nexport type FecBlockProfile = 'iso-ssbg1-v1'\n\n/** Every accepted `fec.blockProfile` token. */\nexport const FEC_BLOCK_PROFILES: readonly FecBlockProfile[] = Object.freeze([\n 'iso-ssbg1-v1',\n])\n\n/** True for a block-profile token this library implements. */\nexport function isFecBlockProfile(value: unknown): value is FecBlockProfile {\n return value === 'iso-ssbg1-v1'\n}\n\n/**\n * Read a required `fec.blockProfile`. Throws on an absent token and on any\n * token other than `iso-ssbg1-v1`: there is no default and nothing is\n * inferred from the wire (draft-ramadan-moq-fec \u00A75.1).\n *\n * @param where the catalog path of the field, for the error message.\n */\nexport function parseFecBlockProfile(value: unknown, where: string): FecBlockProfile {\n if (isFecBlockProfile(value)) return value\n if (value === undefined) {\n throw new Error(`${where} is required: name the FEC block profile (\"iso-ssbg1-v1\")`)\n }\n throw new Error(`${where} must be \"iso-ssbg1-v1\" (got ${JSON.stringify(value)})`)\n}\n\n/** ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet packet length field. */\nexport const SSBG1_LENGTH_FIELD_BYTES = 2\n\n/** The largest protected packet an ssbg_mode1 symbol of size T can carry. */\nexport function ssbg1MaxPacketLength(symbolSize: number): number {\n return symbolSize - SSBG1_LENGTH_FIELD_BYTES\n}\n\n/** Whether a protected packet of `packetLength` bytes fits one T-byte ssbg_mode1 symbol. */\nexport function ssbg1FitsSymbol(packetLength: number, symbolSize: number): boolean {\n return (\n Number.isSafeInteger(packetLength) &&\n packetLength >= 0 &&\n packetLength <= ssbg1MaxPacketLength(symbolSize) &&\n packetLength <= 0xffff\n )\n}\n\n/**\n * Build one ssbg_mode1 source symbol (C.3.3): the protected packet's length\n * in two octets, network byte order, then the packet, then zero octets up to\n * T. The protected packet is the MMTP packet before its Source FEC Payload ID\n * is appended (C.4.2).\n *\n * @throws when `length + 2 > T`: the packet cannot be protected under this\n * profile, and silently truncating it would corrupt every repair symbol.\n */\nexport function buildSsbg1SourceSymbol(packet: Uint8Array, symbolSize: number): Uint8Array {\n if (!Number.isSafeInteger(symbolSize) || symbolSize <= SSBG1_LENGTH_FIELD_BYTES) {\n throw new Error(`ssbg_mode1 symbol size must exceed ${SSBG1_LENGTH_FIELD_BYTES} (got ${symbolSize})`)\n }\n if (!ssbg1FitsSymbol(packet.byteLength, symbolSize)) {\n throw new Error(\n `ssbg_mode1 source symbol needs ${packet.byteLength} + ${SSBG1_LENGTH_FIELD_BYTES} bytes but T is ${symbolSize}`,\n )\n }\n const symbol = new Uint8Array(symbolSize)\n symbol[0] = (packet.byteLength >>> 8) & 0xff\n symbol[1] = packet.byteLength & 0xff\n symbol.set(packet, SSBG1_LENGTH_FIELD_BYTES)\n return symbol\n}\n\n/**\n * Recover the protected packet from one ssbg_mode1 source symbol: the bytes\n * the length field names. Returns null, never a guess, when the length field\n * points past the symbol or any padding octet is non-zero (C.3.3 padding is\n * all 00h), since either means the recovered symbol is not what was sent.\n */\nexport function trimSsbg1SourceSymbol(symbol: Uint8Array): Uint8Array | null {\n if (symbol.byteLength < SSBG1_LENGTH_FIELD_BYTES) return null\n const length = (symbol[0]! << 8) | symbol[1]!\n const end = SSBG1_LENGTH_FIELD_BYTES + length\n if (end > symbol.byteLength) return null\n for (let i = end; i < symbol.byteLength; i++) {\n if (symbol[i] !== 0) return null\n }\n return symbol.subarray(SSBG1_LENGTH_FIELD_BYTES, end)\n}\n\n/** The SS_ID after the last source symbol of `[ssStart, ssStart + ssbLength)`. */\nexport function isoBlockEnd(ssStart: number, ssbLength: number): number {\n return serial32Add(ssStart, ssbLength)\n}\n\n/**\n * Range membership (C.5.2, C.5.3): whether `ssId` is one of the\n * `ssbLength` consecutive SS_IDs starting at `ssStart`, modulo 2^32.\n */\nexport function isoBlockContains(ssStart: number, ssbLength: number, ssId: number): boolean {\n return serial32Distance(ssStart, ssId) < ssbLength\n}\n\n/**\n * A lower bound on how many blocks the block starting at `a` lies behind the\n * one starting at `b` (0 when `a` does not precede `b`). An iso-ssbg1-v1\n * block holds at most `kMax` source symbols (the catalog `sourceSymbols`, the\n * AL-FEC maximum_k_for_repair_flow), so `n * kMax` SS_IDs always span at least\n * `n` blocks. Every interleave horizon in the receiver (decoder retention,\n * worker retirement, ordered-relay settlement) uses this one bound, so the\n * manager's horizon can never fall inside the decoder's.\n */\nexport function isoBlocksBehindLowerBound(a: number, b: number, kMax: number): number {\n if (!Number.isSafeInteger(kMax) || kMax <= 0) {\n throw new Error(`iso-ssbg1-v1 block horizon needs a positive maximum K (got ${kMax})`)\n }\n return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0\n}\n", "/**\n * The draft-ramadan-moq-multicast-01 endpoint and `auth` rules, written once\n * for both sides of the catalog: `validateCmsfMmtpCatalog` in\n * `@blockcast/mmt-container` throws the first issue, and the receivers in\n * `@blockcast/mmt-transport` ignore every endpoint an issue names (all of\n * them when the issue is catalog-level). It lives here because mmt-base is\n * the only package both depend on.\n *\n * Draft: Blockcast/moqcast-draft@fd962fd73d21ed281b60219c11ea61cc9e4e91c4\n * (PR #2 head; normative text equal to 3162ef93), sections 4.1 and 7.2.6.\n */\n\nexport type MulticastJoinMode = \"ssm\" | \"asm\";\n\n/** An endpoint's join mode and `S` (4.1): `sourceAddress` on SSM,\n * `auth.senderAddress` on protected ASM, absent on unprotected ASM. */\nexport interface MulticastEndpointSource {\n\tmode: MulticastJoinMode;\n\tsource?: string;\n}\n\nexport type MulticastCatalogIssueCode =\n\t| \"multicast_auth\"\n\t| \"multicast_endpoint\"\n\t| \"multicast_endpoint_auth\"\n\t| \"multicast_track\";\n\nexport interface MulticastCatalogIssue {\n\tcode: MulticastCatalogIssueCode;\n\t/** JSONPath of the offending field. */\n\tpath: string;\n\tmessage: string;\n\t/** Endpoint the issue makes a receiver ignore; absent for a catalog-level\n\t * issue, which leaves the receiver no endpoint at all (7.2.6). */\n\tendpointIndex?: number;\n}\n\n/** Canonical unpadded base64url of exactly 32 octets. */\nexport const ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;\n\nconst UINT32_MAX = 0xffff_ffff;\nconst AUTH_MEMBERS = new Set([\"scheme\", \"version\", \"sig\", \"keys\", \"minEpoch\"]);\nconst KEY_MEMBERS = new Set([\"kid\", \"publicKey\", \"firstEpoch\"]);\nconst ENDPOINT_AUTH_MEMBERS = new Set([\n\t\"manifestPort\",\n\t\"streamId\",\n\t\"senderAddress\",\n\t\"maxAuthDelayMs\",\n\t\"manifestTimerMs\",\n\t\"manifestTrack\",\n]);\n\ntype JsonObject = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction isIntegerIn(value: unknown, min: number, max: number): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= min && (value as number) <= max;\n}\n\n/**\n * Join mode and `S` of one endpoint, or `undefined` when its fields disagree\n * and a receiver must ignore it (4.1). `sourceAddress` alone selects the mode;\n * `protocol` may only restate it, so \"amt\" and \"atsc3\" (draft -00) are\n * inconsistent here: a bearer is a `networkSource`, never a protocol.\n */\nexport function multicastEndpointSource(endpoint: unknown): MulticastEndpointSource | undefined {\n\tif (!isObject(endpoint)) return undefined;\n\tconst sourceAddress = endpoint[\"sourceAddress\"];\n\tif (sourceAddress !== undefined && !isNonEmptyString(sourceAddress)) return undefined;\n\tconst mode: MulticastJoinMode = sourceAddress === undefined ? \"asm\" : \"ssm\";\n\tconst protocol = endpoint[\"protocol\"];\n\tif (protocol !== undefined && protocol !== mode) return undefined;\n\tconst auth = endpoint[\"auth\"];\n\tconst senderAddress = isObject(auth) ? auth[\"senderAddress\"] : undefined;\n\tif (mode === \"ssm\") return senderAddress === undefined ? { mode, source: sourceAddress as string } : undefined;\n\tif (auth === undefined) return { mode };\n\treturn isNonEmptyString(senderAddress) ? { mode, source: senderAddress } : undefined;\n}\n\nfunction authIssues(auth: unknown): MulticastCatalogIssue[] {\n\tconst base = \"$.multicast.auth\";\n\tconst issue = (path: string, message: string): MulticastCatalogIssue[] => [\n\t\t{ code: \"multicast_auth\", path: `${base}${path}`, message },\n\t];\n\tif (!isObject(auth)) return issue(\"\", \"auth must be an object\");\n\tconst unknown = Object.keys(auth).filter(key => !AUTH_MEMBERS.has(key));\n\tif (unknown.length > 0) {\n\t\treturn issue(\"\", `auth has unknown member(s) ${unknown.join(\", \")}; draft -01 7.2.6 defines scheme, version, sig, keys and minEpoch`);\n\t}\n\tif (auth[\"scheme\"] !== \"bc-provenance\") return issue(\".scheme\", 'scheme must be \"bc-provenance\"');\n\tif (auth[\"version\"] !== 2) return issue(\".version\", \"version must be 2 (bc-provenance version 2, draft -01 7.2)\");\n\tif (auth[\"sig\"] !== \"ed25519\") return issue(\".sig\", 'sig must be \"ed25519\"');\n\tconst keys = auth[\"keys\"];\n\tif (!Array.isArray(keys) || keys.length < 1 || keys.length > 4) {\n\t\treturn issue(\".keys\", \"keys must be an array of one to four {kid, publicKey, firstEpoch} objects\");\n\t}\n\tconst kids = new Set<number>();\n\tconst firstEpochs = new Set<number>();\n\tfor (let index = 0; index < keys.length; index++) {\n\t\tconst key = keys[index];\n\t\tconst at = `.keys[${index}]`;\n\t\tif (!isObject(key) || Object.keys(key).some(member => !KEY_MEMBERS.has(member))) {\n\t\t\treturn issue(at, \"a key is exactly {kid, publicKey, firstEpoch}\");\n\t\t}\n\t\tif (!isIntegerIn(key[\"kid\"], 0, UINT32_MAX)) return issue(`${at}.kid`, \"kid must be an unsigned 32-bit integer\");\n\t\tif (typeof key[\"publicKey\"] !== \"string\" || !ED25519_PUBLIC_KEY_BASE64URL.test(key[\"publicKey\"])) {\n\t\t\treturn issue(`${at}.publicKey`, \"publicKey must be a 32-octet Ed25519 key in canonical unpadded base64url\");\n\t\t}\n\t\tif (!isIntegerIn(key[\"firstEpoch\"], 0, UINT32_MAX)) {\n\t\t\treturn issue(`${at}.firstEpoch`, \"firstEpoch must be an unsigned 32-bit integer\");\n\t\t}\n\t\t// A repeated kid or firstEpoch leaves no key set (7.2.6): fail the whole\n\t\t// block, never fall back to unauthenticated reception.\n\t\tif (kids.has(key[\"kid\"])) return issue(`${at}.kid`, `kid ${key[\"kid\"]} is repeated; the catalog holds no key set`);\n\t\tif (firstEpochs.has(key[\"firstEpoch\"])) {\n\t\t\treturn issue(`${at}.firstEpoch`, `firstEpoch ${key[\"firstEpoch\"]} is repeated; the catalog holds no key set`);\n\t\t}\n\t\tkids.add(key[\"kid\"]);\n\t\tfirstEpochs.add(key[\"firstEpoch\"]);\n\t}\n\tif (!isIntegerIn(auth[\"minEpoch\"], 0, UINT32_MAX)) {\n\t\treturn issue(\".minEpoch\", \"minEpoch must be an unsigned 32-bit integer\");\n\t}\n\treturn [];\n}\n\nfunction endpointAuthIssue(\n\tendpoint: JsonObject,\n\tmode: MulticastJoinMode,\n\tat: string,\n): { path: string; message: string } | undefined {\n\tconst auth = endpoint[\"auth\"];\n\tif (!isObject(auth)) return { path: `${at}.auth`, message: \"auth must be an object\" };\n\tconst unknown = Object.keys(auth).filter(key => !ENDPOINT_AUTH_MEMBERS.has(key));\n\tif (unknown.length > 0) return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(\", \")}` };\n\tif (!isIntegerIn(auth[\"manifestPort\"], 1, 65535)) {\n\t\treturn { path: `${at}.auth.manifestPort`, message: \"manifestPort must be an integer from 1 to 65535\" };\n\t}\n\tif (!isIntegerIn(auth[\"streamId\"], 0, UINT32_MAX)) {\n\t\treturn { path: `${at}.auth.streamId`, message: \"streamId must be an unsigned 32-bit integer\" };\n\t}\n\tif (mode === \"ssm\" && auth[\"senderAddress\"] !== undefined) {\n\t\treturn { path: `${at}.auth.senderAddress`, message: \"senderAddress must not appear on an SSM endpoint; S is sourceAddress\" };\n\t}\n\tif (mode === \"asm\" && !isNonEmptyString(auth[\"senderAddress\"])) {\n\t\treturn { path: `${at}.auth.senderAddress`, message: \"a protected ASM endpoint requires senderAddress\" };\n\t}\n\tif (auth[\"maxAuthDelayMs\"] !== undefined && !isIntegerIn(auth[\"maxAuthDelayMs\"], 50, 1000)) {\n\t\treturn { path: `${at}.auth.maxAuthDelayMs`, message: \"maxAuthDelayMs must be an integer from 50 to 1000\" };\n\t}\n\tif (auth[\"manifestTimerMs\"] !== undefined && !isIntegerIn(auth[\"manifestTimerMs\"], 1, Number.MAX_SAFE_INTEGER)) {\n\t\treturn { path: `${at}.auth.manifestTimerMs`, message: \"manifestTimerMs must be an integer of at least 1\" };\n\t}\n\tif (auth[\"manifestTrack\"] !== undefined && !isNonEmptyString(auth[\"manifestTrack\"])) {\n\t\treturn { path: `${at}.auth.manifestTrack`, message: \"manifestTrack must be a non-empty string\" };\n\t}\n\tif (!isIntegerIn(endpoint[\"bandwidth\"], 1, Number.MAX_SAFE_INTEGER)) {\n\t\treturn { path: `${at}.bandwidth`, message: \"a protected endpoint requires bandwidth, which bounds the receiver's hold queues\" };\n\t}\n\treturn undefined;\n}\n\n/**\n * Where a catalog object carries the namespace of the catalog track it was\n * read from. A symbol, so it is never wire data and survives object spread;\n * `Symbol.for`, so two bundled copies of this module agree on it.\n */\nexport const CATALOG_NAMESPACE: unique symbol = Symbol.for(\"@blockcast/mmt-base/catalogNamespace\");\n\n/**\n * `catalog` tagged with the namespace of its catalog track. Every receiver\n * rule that takes the catalog (`multicastCatalogIssues`,\n * `admittedMulticastEndpoints` and the transport selectors built on them)\n * then judges the foreign-namespace rule against it, independent of track\n * order. Tag once, where the catalog track is read.\n */\nexport function withCatalogNamespace<T extends object>(catalog: T, namespace: string): T {\n\treturn { ...catalog, [CATALOG_NAMESPACE]: namespace };\n}\n\n/**\n * Every draft -01 4.1 / 7.2.6 violation in `catalog.multicast`, in document\n * order. Packet-id, group-address and track-existence rules are not here; the\n * container validator and the receivers already check those.\n *\n * `catalogNamespace` is the namespace of the catalog track, from `options` or else the\n * catalog's `withCatalogNamespace` tag. Without it the\n * foreign-namespace rule can only compare listed tracks with each other: the\n * first listed track's namespace becomes the reference, so which side of a\n * disagreement is dropped depends on order. It still fails closed; pass\n * `catalogNamespace` wherever the catalog track is known.\n */\nexport function multicastCatalogIssues(\n\tcatalog: unknown,\n\toptions: { catalogNamespace?: string } = {},\n): MulticastCatalogIssue[] {\n\tif (!isObject(catalog)) return [];\n\tconst issues: MulticastCatalogIssue[] = [];\n\tif (\"multicast.auth\" in catalog) {\n\t\t// Fail closed: a draft -00 publisher meant the flow to be protected, and\n\t\t// the key it names verifies nothing in bc-provenance version 2.\n\t\tissues.push({\n\t\t\tcode: \"multicast_auth\",\n\t\t\tpath: '$[\"multicast.auth\"]',\n\t\t\tmessage: 'the root \"multicast.auth\" key is retired; declare auth as multicast.auth (draft -01 7.2.6)',\n\t\t});\n\t}\n\tconst multicast = catalog[\"multicast\"];\n\tif (!isObject(multicast)) return issues;\n\tconst protectedCatalog = multicast[\"auth\"] !== undefined;\n\tif (protectedCatalog) issues.push(...authIssues(multicast[\"auth\"]));\n\tconst endpoints = multicast[\"endpoints\"];\n\tif (!Array.isArray(endpoints)) return issues;\n\n\tconst tracks = Array.isArray(catalog[\"tracks\"]) ? catalog[\"tracks\"] : [];\n\tconst namespaceOf = new Map<string, unknown>();\n\tfor (const track of tracks) {\n\t\tif (isObject(track) && typeof track[\"name\"] === \"string\") namespaceOf.set(track[\"name\"], track[\"namespace\"]);\n\t}\n\tconst tagged = (catalog as { [CATALOG_NAMESPACE]?: unknown })[CATALOG_NAMESPACE];\n\tlet referenceNamespace = options.catalogNamespace ?? (typeof tagged === \"string\" ? tagged : undefined);\n\n\tconst sources: Array<MulticastEndpointSource | undefined> = [];\n\tfor (let index = 0; index < endpoints.length; index++) {\n\t\tconst endpoint = endpoints[index];\n\t\tconst at = `$.multicast.endpoints[${index}]`;\n\t\tconst fail = (code: MulticastCatalogIssueCode, path: string, message: string) => {\n\t\t\tissues.push({ code, path, message, endpointIndex: index });\n\t\t};\n\t\tsources.push(undefined);\n\t\tif (!isObject(endpoint)) {\n\t\t\tfail(\"multicast_endpoint\", at, \"endpoint must be an object\");\n\t\t\tcontinue;\n\t\t}\n\t\tconst protocol = endpoint[\"protocol\"];\n\t\tif (protocol !== undefined && protocol !== \"ssm\" && protocol !== \"asm\") {\n\t\t\tfail(\"multicast_endpoint\", `${at}.protocol`, `protocol must be \"ssm\" or \"asm\"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -01 4.2)`);\n\t\t\tcontinue;\n\t\t}\n\t\tconst hasAuth = endpoint[\"auth\"] !== undefined;\n\t\tif (protectedCatalog !== hasAuth) {\n\t\t\tfail(\n\t\t\t\t\"multicast_endpoint_auth\",\n\t\t\t\t`${at}.auth`,\n\t\t\t\tprotectedCatalog\n\t\t\t\t\t? \"every endpoint requires auth when multicast carries auth; a receiver ignores this endpoint\"\n\t\t\t\t\t: \"auth names no key without multicast.auth; a receiver ignores this endpoint\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst sourceAddress = endpoint[\"sourceAddress\"];\n\t\tif (sourceAddress !== undefined && !isNonEmptyString(sourceAddress)) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.sourceAddress`, \"sourceAddress must be a non-empty string\");\n\t\t\tcontinue;\n\t\t}\n\t\tconst mode: MulticastJoinMode = sourceAddress === undefined ? \"asm\" : \"ssm\";\n\t\tif (protocol !== undefined && protocol !== mode) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.protocol`, `protocol ${JSON.stringify(protocol)} disagrees with sourceAddress, which selects ${mode}`);\n\t\t\tcontinue;\n\t\t}\n\t\t// The (S, G, port) rule below compares ports with ===, so only an\n\t\t// integer may reach it: \"5001\" and 5001 would otherwise both pass.\n\t\tif (!isIntegerIn(endpoint[\"port\"], 1, 65535)) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.port`, \"port must be an integer from 1 to 65535\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (hasAuth) {\n\t\t\tconst authIssue = endpointAuthIssue(endpoint, mode, at);\n\t\t\tif (authIssue) {\n\t\t\t\tfail(\"multicast_endpoint_auth\", authIssue.path, authIssue.message);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tsources[index] = multicastEndpointSource(endpoint);\n\n\t\tconst refs = Array.isArray(endpoint[\"tracks\"]) ? endpoint[\"tracks\"] : [];\n\t\tfor (let refIndex = 0; refIndex < refs.length; refIndex++) {\n\t\t\tconst ref = refs[refIndex];\n\t\t\tconst name = isObject(ref) ? ref[\"name\"] : ref;\n\t\t\tif (typeof name !== \"string\") continue;\n\t\t\tconst namespace = namespaceOf.get(name);\n\t\t\tif (typeof namespace !== \"string\") continue;\n\t\t\treferenceNamespace ??= namespace;\n\t\t\tif (namespace !== referenceNamespace) {\n\t\t\t\tfail(\"multicast_track\", `${at}.tracks[${refIndex}].name`, `track ${JSON.stringify(name)} declares namespace ${JSON.stringify(namespace)}, not the catalog track's ${JSON.stringify(referenceNamespace)}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// One endpoint object per (S, G, port), and a manifestPort that is no port\n\t// and no other manifestPort of its (S, G) (4.1, 7.2.6). The rule binds the\n\t// endpoint whose manifestPort clashes; two equal tuples or two equal\n\t// manifestPorts break it on both sides, so both are named.\n\tconst tupleOf = (index: number) => {\n\t\tconst endpoint = endpoints[index] as JsonObject;\n\t\tconst auth = endpoint[\"auth\"];\n\t\treturn {\n\t\t\tsg: JSON.stringify([sources[index]!.source ?? null, endpoint[\"groupAddress\"]]),\n\t\t\tport: endpoint[\"port\"],\n\t\t\tmanifestPort: isObject(auth) ? auth[\"manifestPort\"] : undefined,\n\t\t};\n\t};\n\tfor (let index = 0; index < endpoints.length; index++) {\n\t\tif (!sources[index]) continue;\n\t\tconst own = tupleOf(index);\n\t\tconst at = `$.multicast.endpoints[${index}]`;\n\t\tif (own.manifestPort !== undefined && own.manifestPort === own.port) {\n\t\t\tissues.push({ code: \"multicast_endpoint_auth\", path: `${at}.auth.manifestPort`, message: \"manifestPort must differ from the endpoint's own port\", endpointIndex: index });\n\t\t\tcontinue;\n\t\t}\n\t\tfor (let other = 0; other < endpoints.length; other++) {\n\t\t\tif (other === index || !sources[other]) continue;\n\t\t\tconst peer = tupleOf(other);\n\t\t\tif (peer.sg !== own.sg) continue;\n\t\t\tif (peer.port === own.port) {\n\t\t\t\tissues.push({ code: \"multicast_endpoint\", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (own.manifestPort !== undefined && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {\n\t\t\t\tissues.push({ code: \"multicast_endpoint_auth\", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn issues;\n}\n\n/**\n * Indices of the endpoints a receiver may use: none when a catalog-level\n * issue leaves it no key set (7.2.6), otherwise every endpoint no issue\n * names. Other receiver checks (group address, packet ids) still apply.\n */\nexport function admittedMulticastEndpoints(\n\tcatalog: unknown,\n\toptions: { catalogNamespace?: string } = {},\n): ReadonlySet<number> {\n\tconst multicast = isObject(catalog) ? catalog[\"multicast\"] : undefined;\n\tconst endpoints = isObject(multicast) && Array.isArray(multicast[\"endpoints\"]) ? multicast[\"endpoints\"] : [];\n\tconst issues = multicastCatalogIssues(catalog, options);\n\tif (issues.some(issue => issue.endpointIndex === undefined)) return new Set();\n\tconst ignored = new Set(issues.map(issue => issue.endpointIndex));\n\treturn new Set(endpoints.map((_, index) => index).filter(index => !ignored.has(index)));\n}\n", "/**\n * Runtime FEC bindings (BLO-50171): what each source track's FEC resolves to\n * for this library's receiver, read from `validateFecCatalog`'s per-instance\n * result and never re-derived.\n *\n * Every runtime reader (the container's `resolveCatalogTrackFec`,\n * `resolveRepairLayers`, `MulticastManager.configureMultiTrack`, transport's\n * `catalogRepairTracks`) goes through `resolveFecBindings`, so they cannot\n * disagree with the validator or with each other about which tracks have FEC\n * and which repair tracks belong to them.\n *\n * The draft-ramadan-moq-fec-catalog-00 array is the only shape. Both catalog\n * dialects bind through it: an MSF `tracks[]` entry directly, and a hang\n * `video`/`audio` rendition (`fec` the same array) with its `repair.renditions`\n * entries (`source` + `instanceId`), mapped onto a `tracks[]` view so the same\n * validator runs. A hang rendition is authoritative over a `tracks[]` entry of\n * the same name.\n *\n * Fail closed per instance: a source whose base instance is not `ok` resolves\n * to `unavailable` with the validator's code and path, and costs no other\n * track anything. A draft-01 object `fec` is `fec_member` at that track's\n * `fec`: the track plays without FEC.\n */\nimport { validateFecCatalog, type FecCatalogCode, type FecCatalogReceiver } from './fec-catalog.js'\n\n/** The receiver this runtime is: it decodes `iso-ssbg1-v1` only. */\nexport const RUNTIME_FEC_RECEIVER: FecCatalogReceiver = Object.freeze({\n schemes: Object.freeze(['raptorq', 'reed-solomon']),\n blockProfiles: Object.freeze(['iso-ssbg1-v1']),\n overlays: true,\n})\n\n/** Repair track priority when the track states none (MoQT scale, 192..255). */\nexport const DEFAULT_FEC_REPAIR_PRIORITY = 240\n\n/** One `ok` instance's geometry. */\nexport interface FecInstanceGeometry {\n readonly instanceId: number\n readonly scheme: 'raptorq' | 'reed-solomon'\n /** `iso-ssbg1-v1` under `RUNTIME_FEC_RECEIVER`. */\n readonly blockProfile: string\n /** K. */\n readonly sourceSymbols: number\n /** P_0 for a base instance; the overlay's own P for an overlay. */\n readonly repairSymbols: number\n /** T. */\n readonly symbolSize: number\n readonly interleaveDepthMs?: number\n readonly reorderToleranceMs?: number\n readonly repairTrack: string\n readonly symbolTrack?: string\n}\n\nexport interface FecLayerBinding {\n readonly layer: number\n readonly repairTrackName: string\n readonly repairSymbols: number\n /** O_i = sum(P_j, j < i). */\n readonly rsIdOffset: number\n readonly priority: number\n}\n\nexport interface FecOverlayBinding {\n readonly repairTrackName: string\n readonly scope: 'keyframe'\n readonly sourceSymbols: number\n readonly repairSymbols: number\n readonly priority: number\n readonly geometry: FecInstanceGeometry\n}\n\nexport interface FecBindingUnavailable {\n /** The validator's code, or `fec_base_instances` for a track with no base\n * instance or more than one `ok` one (the MMTP wire carries one base Source FEC Payload\n * ID, mmt-02 7.4.1). */\n readonly code: FecCatalogCode | 'fec_base_instances'\n /** JSON Pointer into the catalog (a hang path starts `/video` or `/audio`). */\n readonly path: string\n readonly outcome: 'unavailable' | 'violation'\n}\n\nexport type FecSourceBinding =\n | {\n readonly status: 'ok'\n readonly track: string\n /** JSON Pointer of the source track (`/tracks/<i>`, `/video/renditions/<name>`). */\n readonly pointer: string\n readonly base: FecInstanceGeometry\n /** Base layers in order, layer 0 first. */\n readonly layers: readonly FecLayerBinding[]\n /** Sum of P over the base layers: the wire RSB_length. */\n readonly totalBaseRepairSymbols: number\n readonly overlay?: FecOverlayBinding\n /** Why a declared overlay is not used, when one is not. */\n readonly overlayUnavailable?: FecBindingUnavailable\n }\n | {\n readonly status: 'unavailable'\n readonly track: string\n readonly pointer: string\n readonly reason: FecBindingUnavailable\n }\n\ntype Obj = Record<string, unknown>\nconst recordOf = (v: unknown): Obj | undefined =>\n typeof v === 'object' && v !== null && !Array.isArray(v) ? (v as Obj) : undefined\nconst escape = (s: string) => s.replace(/~/g, '~0').replace(/\\//g, '~1')\n\n/**\n * The `tracks[]` view of a catalog in either dialect, with each view track's\n * JSON Pointer in the original catalog.\n */\nexport function fecCatalogTracks(catalog: unknown): { tracks: unknown[]; pointers: string[] } {\n const root = recordOf(catalog) ?? {}\n const tracks: unknown[] = []\n const pointers: string[] = []\n const hangNames = new Set<string>()\n for (const role of ['video', 'audio'] as const) {\n for (const [name, r] of Object.entries(recordOf(recordOf(root[role])?.['renditions']) ?? {})) {\n const entry = recordOf(r)\n if (!entry) continue\n hangNames.add(name)\n const kind = recordOf(entry['container'])?.['kind']\n // A hang rendition carries no `packaging`; its container kind is it.\n // `mmtp-fec` is hang's kind for MMTP that carries FEC (the FFmpeg\n // publisher's `moq_publish_set_video_packaging`): MMTP packaging.\n const view: Obj = { name, packaging: kind === undefined || kind === 'mmtp-fec' ? 'mmtp' : kind }\n if (entry['fec'] !== undefined) view['fec'] = entry['fec']\n tracks.push(view)\n pointers.push(`/${role}/renditions/${escape(name)}`)\n }\n }\n const hangRenditions = tracks.length\n for (const [name, r] of Object.entries(recordOf(recordOf(root['repair'])?.['renditions']) ?? {})) {\n hangNames.add(name)\n tracks.push({ packaging: 'fec-repair', ...recordOf(r), name })\n pointers.push(`/repair/renditions/${escape(name)}`)\n }\n const raw = Array.isArray(root['tracks']) ? root['tracks'] : []\n const named = new Set(hangNames)\n raw.forEach((t, i) => {\n const name = recordOf(t)?.['name']\n if (typeof name === 'string') named.add(name)\n if (typeof name === 'string' && hangNames.has(name)) return\n tracks.push(t)\n pointers.push(`/tracks/${i}`)\n })\n // hang layer 0 needs no `repair.renditions` entry: the rendition's instance\n // names it, and a hang Root (hang's toHang) carries no repair block. Such a\n // name stands for a repair track of that instance.\n tracks.slice(0, hangRenditions).forEach((t, ti) => {\n const view = t as Obj\n if (!Array.isArray(view['fec'])) return\n view['fec'].forEach((o, ii) => {\n const repairTrack = recordOf(o)?.['repairTrack']\n if (typeof repairTrack !== 'string' || named.has(repairTrack)) return\n named.add(repairTrack)\n const instanceId = recordOf(o)?.['instanceId']\n tracks.push({ name: repairTrack, packaging: 'fec-repair', source: view['name'], ...(instanceId === undefined ? {} : { instanceId }) })\n pointers.push(`${pointers[ti]}/fec/${ii}/repairTrack`)\n })\n })\n return { tracks, pointers }\n}\n\n/**\n * Every source track's runtime FEC binding, by track name. A track with no\n * `fec`, or an empty `fec` array, declares no FEC and has no entry.\n */\nexport function resolveFecBindings(\n catalog: unknown,\n receiver: FecCatalogReceiver = RUNTIME_FEC_RECEIVER,\n): ReadonlyMap<string, FecSourceBinding> {\n const { tracks, pointers } = fecCatalogTracks(catalog)\n const report = validateFecCatalog({ tracks }, receiver)\n const at = (path: string) => {\n const m = /^\\/tracks\\/(\\d+)(.*)$/.exec(path)\n return m ? pointers[Number(m[1])] + m[2] : path\n }\n const trackAt = (i: number) => recordOf(tracks[i]) ?? {}\n const nameAt = (i: number | null) => (i === null ? undefined : (trackAt(i)['name'] as string))\n const why = (i: { outcome: string; issues: readonly { code: FecCatalogCode; path: string }[]; path: string }) =>\n Object.freeze({\n code: i.issues[0]?.code ?? 'fec_member',\n path: at(i.issues[0]?.path ?? i.path),\n outcome: i.outcome === 'unavailable' ? ('unavailable' as const) : ('violation' as const),\n })\n\n const out = new Map<string, FecSourceBinding>()\n tracks.forEach((t, ti) => {\n const track = recordOf(t)\n const name = track?.['name']\n if (!track || typeof name !== 'string' || out.has(name)) return\n if (track['packaging'] === 'fec-repair' || track['packaging'] === 'fec-source') return\n const fec = track['fec']\n if (fec === undefined) return\n if (!Array.isArray(fec)) {\n const reason = { code: 'fec_member' as const, path: at(`/tracks/${ti}/fec`), outcome: 'violation' as const }\n out.set(name, Object.freeze({ status: 'unavailable', track: name, pointer: pointers[ti]!, reason: Object.freeze(reason) }))\n return\n }\n if (fec.length === 0) return\n const insts = report.instances.filter((i) => i.trackIndex === ti)\n const isOverlay = (i: (typeof insts)[number]) => Object.hasOwn(recordOf(fec[Number(i.path.split('/').pop())]) ?? {}, 'scope')\n const bases = insts.filter((i) => !isOverlay(i))\n const okBases = bases.filter((i) => i.outcome === 'ok')\n if (okBases.length !== 1) {\n const reason =\n // No base instance at all (overlay-only `fec`) is a count of 0, not\n // the overlay's own issue.\n okBases.length > 1 || bases.length === 0\n ? Object.freeze({ code: 'fec_base_instances' as const, path: at(`/tracks/${ti}/fec`), outcome: 'violation' as const })\n : why(bases[0] ?? insts[0]!)\n out.set(name, Object.freeze({ status: 'unavailable', track: name, pointer: pointers[ti]!, reason }))\n return\n }\n const b = okBases[0]!\n const base = geometry(recordOf(fec[Number(b.path.split('/').pop())])!, b.instanceId!, nameAt(b.symbolTrack))\n const priorityOf = (i: number | null) => {\n const p = i === null ? undefined : trackAt(i)['priority']\n return typeof p === 'number' ? p : DEFAULT_FEC_REPAIR_PRIORITY\n }\n const layers: FecLayerBinding[] = [\n { layer: 0, repairTrackName: base.repairTrack, repairSymbols: base.repairSymbols, rsIdOffset: 0, priority: priorityOf(b.repairTrack) },\n ]\n const more = b.boundRepairTracks\n .map((tj) => ({ tj, layer: trackAt(tj)['repairLayer'] as number }))\n .sort((x, y) => x.layer - y.layer)\n let offset = base.repairSymbols\n for (const { tj, layer } of more) {\n const repairSymbols = trackAt(tj)['repairSymbols'] as number\n layers.push({ layer, repairTrackName: nameAt(tj)!, repairSymbols, rsIdOffset: offset, priority: priorityOf(tj) })\n offset += repairSymbols\n }\n let overlay: FecOverlayBinding | undefined\n let overlayUnavailable: FecBindingUnavailable | undefined\n const ov = insts.find(isOverlay)\n if (ov?.outcome === 'ok') {\n const g = geometry(recordOf(fec[Number(ov.path.split('/').pop())])!, ov.instanceId!, nameAt(ov.symbolTrack))\n overlay = Object.freeze({\n repairTrackName: g.repairTrack,\n scope: 'keyframe' as const,\n sourceSymbols: g.sourceSymbols,\n repairSymbols: g.repairSymbols,\n priority: priorityOf(ov.repairTrack),\n geometry: g,\n })\n } else if (ov) overlayUnavailable = why(ov)\n out.set(\n name,\n Object.freeze({\n status: 'ok' as const,\n track: name,\n pointer: pointers[ti]!,\n base,\n layers: Object.freeze(layers.map((l) => Object.freeze(l))),\n totalBaseRepairSymbols: offset,\n ...(overlay ? { overlay } : {}),\n ...(overlayUnavailable ? { overlayUnavailable } : {}),\n }),\n )\n })\n return out\n}\n\n// The validator passed this instance for this receiver, so every member read\n// here is present and in range.\nfunction geometry(o: Obj, instanceId: number, symbolTrack: string | undefined): FecInstanceGeometry {\n return Object.freeze({\n instanceId,\n scheme: o['scheme'] as 'raptorq' | 'reed-solomon',\n blockProfile: o['blockProfile'] as string,\n sourceSymbols: o['sourceSymbols'] as number,\n repairSymbols: o['repairSymbols'] as number,\n symbolSize: o['symbolSize'] as number,\n ...(typeof o['interleaveDepthMs'] === 'number' ? { interleaveDepthMs: o['interleaveDepthMs'] } : {}),\n ...(typeof o['reorderToleranceMs'] === 'number' ? { reorderToleranceMs: o['reorderToleranceMs'] } : {}),\n repairTrack: o['repairTrack'] as string,\n ...(symbolTrack !== undefined ? { symbolTrack } : {}),\n })\n}\n", "/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n\n// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.\n// node.js versions earlier than v19 don't declare it in global scope.\n// For node.js, package.json#exports field mapping rewrites import\n// from `crypto` to `cryptoNode`, which imports native module.\n// Makes the utils un-importable in browsers without a bundler.\n// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.\nimport { crypto } from '@noble/hashes/crypto';\n\n/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */\nexport function isBytes(a: unknown): a is Uint8Array {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n\n/** Asserts something is positive integer. */\nexport function anumber(n: number): void {\n if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n);\n}\n\n/** Asserts something is Uint8Array. */\nexport function abytes(b: Uint8Array | undefined, ...lengths: number[]): void {\n if (!isBytes(b)) throw new Error('Uint8Array expected');\n if (lengths.length > 0 && !lengths.includes(b.length))\n throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);\n}\n\n/** Asserts something is hash */\nexport function ahash(h: IHash): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new Error('Hash should be wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n}\n\n/** Asserts a hash instance has not been destroyed / finished */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/** Asserts output is properly-sized byte array */\nexport function aoutput(out: any, instance: any): void {\n abytes(out);\n const min = instance.outputLen;\n if (out.length < min) {\n throw new Error('digestInto() expects output buffer of length at least ' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/** Cast u8 / u16 / u32 to u8. */\nexport function u8(arr: TypedArray): Uint8Array {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** Cast u8 / u16 / u32 to u32. */\nexport function u32(arr: TypedArray): Uint32Array {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n\n/** Zeroize a byte array. Warning: JS provides no guarantees. */\nexport function clean(...arrays: TypedArray[]): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/** Create DataView of an array for easy byte-level manipulation. */\nexport function createView(arr: TypedArray): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** The rotate right (circular right shift) operation for uint32 */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/** The rotate left (circular left shift) operation for uint32 */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Is current platform little-endian? Most are. Big-Endian platform: IBM */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/** The byte swap operation for uint32 */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/** Conditionally byte swap if on a big-endian platform */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n);\n\n/** @deprecated */\nexport const byteSwapIfBE: typeof swap8IfBE = swap8IfBE;\n/** In place byte swap for Uint32Array */\nexport function byteSwap32(arr: Uint32Array): Uint32Array {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n\nexport const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE\n ? (u: Uint32Array) => u\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string. Uses built-in function, when available.\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex: string): Uint8Array {\n if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);\n // @ts-ignore\n if (hasHexBuiltin) return Uint8Array.fromHex(hex);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new Error('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new Error('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * Call of async fn will return Promise, which will be fullfiled only on\n * next scheduler queue processing step and this is exactly what we need.\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/** Returns control to thread each 'tick' ms to avoid blocking. */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\ndeclare const TextDecoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])\n */\nexport function utf8ToBytes(str: string): Uint8Array {\n if (typeof str !== 'string') throw new Error('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/**\n * Converts bytes to string using UTF8 encoding.\n * @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'\n */\nexport function bytesToUtf8(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\n/** Accepted input of hash functions. Strings are converted to byte arrays. */\nexport type Input = string | Uint8Array;\n/**\n * Normalizes (non-hex) string or Uint8Array to Uint8Array.\n * Warning: when Uint8Array is passed, it would NOT get copied.\n * Keep in mind for future mutable operations.\n */\nexport function toBytes(data: Input): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n/**\n * Helper for KDFs: consumes uint8array or string.\n * When string is passed, does utf8 decoding, using TextDecoder.\n */\nexport function kdfInputToBytes(data: KDFInput): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** Copies several Uint8Arrays into one. */\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new Error('options should be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Hash interface. */\nexport type IHash = {\n (data: Uint8Array): Uint8Array;\n blockLen: number;\n outputLen: number;\n create: any;\n};\n\n/** For runtime check if class implements interface */\nexport abstract class Hash<T extends Hash<T>> {\n abstract blockLen: number; // Bytes per block\n abstract outputLen: number; // Bytes in output\n abstract update(buf: Input): this;\n // Writes digest into buf\n abstract digestInto(buf: Uint8Array): void;\n abstract digest(): Uint8Array;\n /**\n * Resets internal state. Makes Hash instance unusable.\n * Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed\n * by user, they will need to manually call `destroy()` when zeroing is necessary.\n */\n abstract destroy(): void;\n /**\n * Clones hash instance. Unsafe: doesn't check whether `to` is valid. Can be used as `clone()`\n * when no options are passed.\n * Reasons to use `_cloneInto` instead of clone: 1) performance 2) reuse instance => all internal\n * buffers are overwritten => causes buffer overwrite which is used for digest in some cases.\n * There are no guarantees for clean-up because it's impossible in JS.\n */\n abstract _cloneInto(to?: T): T;\n // Safe version that clones internal state\n abstract clone(): T;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream\n xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf\n};\n\n/** Hash function */\nexport type CHash = ReturnType<typeof createHasher>;\n/** Hash function with output */\nexport type CHashO = ReturnType<typeof createOptHasher>;\n/** XOF with output */\nexport type CHashXO = ReturnType<typeof createXOFer>;\n\n/** Wraps hash function, creating an interface on top of it */\nexport function createHasher<T extends Hash<T>>(\n hashCons: () => Hash<T>\n): {\n (msg: Input): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(): Hash<T>;\n} {\n const hashC = (msg: Input): Uint8Array => hashCons().update(toBytes(msg)).digest();\n const tmp = hashCons();\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = () => hashCons();\n return hashC;\n}\n\nexport function createOptHasher<H extends Hash<H>, T extends Object>(\n hashCons: (opts?: T) => Hash<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): Hash<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\n\nexport function createXOFer<H extends HashXOF<H>, T extends Object>(\n hashCons: (opts?: T) => HashXOF<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): HashXOF<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\nexport const wrapConstructor: typeof createHasher = createHasher;\nexport const wrapConstructorWithOpts: typeof createOptHasher = createOptHasher;\nexport const wrapXOFConstructorWithOpts: typeof createXOFer = createXOFer;\n\n/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */\nexport function randomBytes(bytesLength = 32): Uint8Array {\n if (crypto && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(bytesLength));\n }\n // Legacy Node.js compatibility\n if (crypto && typeof crypto.randomBytes === 'function') {\n return Uint8Array.from(crypto.randomBytes(bytesLength));\n }\n throw new Error('crypto.getRandomValues must be defined');\n}\n", "/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport { type Input, Hash, abytes, aexists, aoutput, clean, createView, toBytes } from './utils.ts';\n\n/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */\nexport function setBigUint64(\n view: DataView,\n byteOffset: number,\n value: bigint,\n isLE: boolean\n): void {\n if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);\n const _32n = BigInt(32);\n const _u32_max = BigInt(0xffffffff);\n const wh = Number((value >> _32n) & _u32_max);\n const wl = Number(value & _u32_max);\n const h = isLE ? 4 : 0;\n const l = isLE ? 0 : 4;\n view.setUint32(byteOffset + h, wh, isLE);\n view.setUint32(byteOffset + l, wl, isLE);\n}\n\n/** Choice: a ? b : c */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/** Majority function, true if any two inputs is true. */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n */\nexport abstract class HashMD<T extends HashMD<T>> extends Hash<T> {\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n super();\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: Input): this {\n aexists(this);\n data = toBytes(data);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path: we have at least one block in input, cast it to view and process\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\n setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n if (length % blockLen) to.buffer.set(buffer);\n return to;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */\nexport const SHA256_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state. Bits 32..64 of frac part of sqrt of primes 23..53 */\nexport const SHA224_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state. Bits 0..64 of frac part of sqrt of primes 23..53 */\nexport const SHA384_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state. Bits 0..64 of frac part of sqrt of primes 2..19 */\nexport const SHA512_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n", "/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and\n * [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, rotr } from './utils.ts';\n\n/**\n * Round constants:\n * First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable temporary buffer. \"W\" comes straight from spec. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\nexport class SHA256 extends HashMD<SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n\n constructor(outputLen: number = 32) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\nexport class SHA224 extends SHA256 {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// Round contants\n// First 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable temporary buffers\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\nexport class SHA512 extends HashMD<SHA512> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor(outputLen: number = 64) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\nexport class SHA384 extends SHA512 {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See `test/misc/sha2-gen-iv.js`.\n */\n\n/** SHA512/224 IV */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA512/256 IV */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\nexport class SHA512_224 extends SHA512 {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\nexport class SHA512_256 extends SHA512 {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634.\n *\n * It is the fastest JS hash, even faster than Blake3.\n * To break sha256 using birthday attack, attackers need to try 2^128 hashes.\n * BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n */\nexport const sha256: CHash = /* @__PURE__ */ createHasher(() => new SHA256());\n/** SHA2-224 hash function from RFC 4634 */\nexport const sha224: CHash = /* @__PURE__ */ createHasher(() => new SHA224());\n\n/** SHA2-512 hash function from RFC 4634. */\nexport const sha512: CHash = /* @__PURE__ */ createHasher(() => new SHA512());\n/** SHA2-384 hash function from RFC 4634. */\nexport const sha384: CHash = /* @__PURE__ */ createHasher(() => new SHA384());\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_256: CHash = /* @__PURE__ */ createHasher(() => new SHA512_256());\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_224: CHash = /* @__PURE__ */ createHasher(() => new SHA512_224());\n", "/**\n * SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.\n *\n * To break sha256 using birthday attack, attackers need to try 2^128 hashes.\n * BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n *\n * Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\n * @module\n * @deprecated\n */\nimport {\n SHA224 as SHA224n,\n sha224 as sha224n,\n SHA256 as SHA256n,\n sha256 as sha256n,\n} from './sha2.ts';\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA256: typeof SHA256n = SHA256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha256: typeof sha256n = sha256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA224: typeof SHA224n = SHA224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha224: typeof sha224n = sha224n;\n", "import { sha256 } from '@noble/hashes/sha256'\n\n/** Fixed-width bytes before a record's digest list. */\nexport const BCPV2_HEADER_LENGTH = 68\nexport const BCPV2_SIGNATURE_LENGTH = 64\nexport const BCPV2_MIN_RECORD_LENGTH = BCPV2_HEADER_LENGTH + 32 + BCPV2_SIGNATURE_LENGTH\nexport const BCPV2_MAX_RECORD_LENGTH = 1316\nexport const BCPV2_MAX_DIGESTS = 37\nexport const BCPV2_HASH_ALG_SHA256 = 1\n\nexport interface BcProvenanceV2Endpoint {\n /** S in the AMBI pseudoheader and signature context. */\n sourceAddress: string\n /** G in the AMBI pseudoheader and signature context. */\n groupAddress: string\n /** UDP source and destination port of protected datagrams. */\n port: number\n /** UDP destination port carrying manifest records. */\n manifestPort: number\n /** Catalog-declared channel generation. */\n streamId: number\n}\n\nexport interface BcProvenanceV2Record {\n kid: number\n epoch: number\n mseq: bigint\n firstSeq: bigint\n count: number\n hashAlg: number\n flags: number\n sent: number\n ctx: Uint8Array\n digests: Uint8Array[]\n signature: Uint8Array\n /** Record bytes covered by the signature, excluding the signature itself. */\n body: Uint8Array\n /** A defensive copy of the complete wire record. */\n raw: Uint8Array\n}\n\nconst text = new TextEncoder()\nconst MAGIC = Uint8Array.from([0x4d, 0x48, 0x4d, 0x32]) // MHM2\nconst SIGNING_DOMAIN = text.encode('mcast-manifest-v2\\0')\n\nfunction equalBytes(left: Uint8Array, right: Uint8Array): boolean {\n if (left.byteLength !== right.byteLength) return false\n let different = 0\n for (let index = 0; index < left.byteLength; index++) different |= left[index]! ^ right[index]!\n return different === 0\n}\n\nfunction u16(value: number): Uint8Array {\n const result = new Uint8Array(2)\n new DataView(result.buffer).setUint16(0, value)\n return result\n}\n\nfunction u32(value: number): Uint8Array {\n const result = new Uint8Array(4)\n new DataView(result.buffer).setUint32(0, value)\n return result\n}\n\nfunction validU32(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffff_ffff\n}\n\nfunction validPort(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffff\n}\n\nfunction parseIpv4(address: string): Uint8Array {\n const fields = address.split('.')\n if (fields.length !== 4 || fields.some((field) => !/^\\d+$/.test(field))) {\n throw new Error(`invalid IPv4 address ${address}`)\n }\n const values = fields.map(Number)\n if (values.some((value, index) => value > 255 || String(value) !== fields[index])) {\n throw new Error(`invalid IPv4 address ${address}`)\n }\n return Uint8Array.from(values)\n}\n\nfunction parseIpv6(address: string): Uint8Array {\n const compressed = address.includes('::')\n if ((address.match(/::/g) ?? []).length > 1) throw new Error(`invalid IPv6 address ${address}`)\n const [leftText, rightText = ''] = address.split('::')\n const left = leftText ? leftText.split(':') : []\n const right = rightText ? rightText.split(':') : []\n // Native-width IPv6 is required by the draft; embedded IPv4 text is not.\n if (left.some((group) => group.includes('.')) || right.some((group) => group.includes('.'))) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const missing = 8 - left.length - right.length\n if (missing < 0 || (!compressed && missing !== 0) || (compressed && missing < 1)) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const groups = [...left, ...Array.from({ length: missing }, () => '0'), ...right]\n if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/i.test(group))) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const result = new Uint8Array(16)\n const view = new DataView(result.buffer)\n groups.forEach((group, index) => view.setUint16(index * 2, Number.parseInt(group, 16)))\n return result\n}\n\n/** Parse an IPv4 or IPv6 address in the native-width form used on the wire. */\nexport function parseBcProvenanceV2Address(address: string): Uint8Array {\n if (address.includes(':')) return parseIpv6(address)\n return parseIpv4(address)\n}\n\nfunction endpointBytes(endpoint: BcProvenanceV2Endpoint): {\n source: Uint8Array\n group: Uint8Array\n} {\n if (!validPort(endpoint.port) || !validPort(endpoint.manifestPort) || !validU32(endpoint.streamId)) {\n throw new Error('invalid BCPV2 endpoint')\n }\n const source = parseBcProvenanceV2Address(endpoint.sourceAddress)\n const group = parseBcProvenanceV2Address(endpoint.groupAddress)\n if (source.byteLength !== group.byteLength) throw new Error('endpoint address families differ')\n return { source, group }\n}\n\nfunction readU32(view: DataView, offset: number): number {\n return view.getUint32(offset)\n}\n\nfunction readU64(view: DataView, offset: number): bigint {\n return view.getBigUint64(offset)\n}\n\n/**\n * Decode the fixed-layout BCPV2 record from multicast-01 Section 7.2.3.\n * Structural validation happens before callers inspect the variable digest list.\n */\nexport function decodeBcProvenanceV2Record(input: Uint8Array): BcProvenanceV2Record {\n const bytes = Uint8Array.from(input)\n if (\n bytes.byteLength < BCPV2_MIN_RECORD_LENGTH ||\n bytes.byteLength > BCPV2_MAX_RECORD_LENGTH ||\n (bytes.byteLength - 132) % 32 !== 0\n ) {\n throw new Error('invalid BCPV2 record length')\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n if (!equalBytes(bytes.subarray(0, 4), MAGIC)) throw new Error('invalid BCPV2 record magic')\n const count = view.getUint16(28)\n if (count < 1 || count > BCPV2_MAX_DIGESTS || bytes.byteLength !== 132 + count * 32) {\n throw new Error('invalid BCPV2 record count')\n }\n const body = bytes.slice(0, bytes.byteLength - BCPV2_SIGNATURE_LENGTH)\n const digests: Uint8Array[] = []\n for (let index = 0; index < count; index++) {\n digests.push(bytes.slice(BCPV2_HEADER_LENGTH + index * 32, BCPV2_HEADER_LENGTH + (index + 1) * 32))\n }\n return {\n kid: readU32(view, 4),\n epoch: readU32(view, 8),\n mseq: readU64(view, 12),\n firstSeq: readU64(view, 20),\n count,\n hashAlg: bytes[30]!,\n flags: bytes[31]!,\n sent: readU32(view, 32),\n ctx: bytes.slice(36, 68),\n digests,\n signature: bytes.slice(bytes.byteLength - BCPV2_SIGNATURE_LENGTH),\n body,\n raw: bytes,\n }\n}\n\n/** Return the exact Ed25519 preimage from multicast-01 Section 7.2.4. */\nexport function bcProvenanceV2SignedMessage(\n record: BcProvenanceV2Record,\n endpoint: BcProvenanceV2Endpoint,\n): Uint8Array {\n const { source, group } = endpointBytes(endpoint)\n const prefix = new Uint8Array(SIGNING_DOMAIN.byteLength + 4 + source.byteLength + group.byteLength + 2)\n let offset = 0\n prefix.set(SIGNING_DOMAIN, offset)\n offset += SIGNING_DOMAIN.byteLength\n prefix.set(u32(endpoint.streamId), offset)\n offset += 4\n prefix.set(source, offset)\n offset += source.byteLength\n prefix.set(group, offset)\n offset += group.byteLength\n prefix.set(u16(endpoint.manifestPort), offset)\n const message = new Uint8Array(prefix.byteLength + record.body.byteLength)\n message.set(prefix)\n message.set(record.body, prefix.byteLength)\n return message\n}\n\n/**\n * Compute the AMBI Section 3.4.2 digest for one transmitted datagram.\n * The payload includes the Source FEC Payload ID when one was transmitted.\n */\nexport function bcProvenanceV2DatagramDigest(\n endpoint: BcProvenanceV2Endpoint,\n payload: Uint8Array,\n): Uint8Array {\n if (payload.byteLength > 0xffff) throw new Error('datagram payload exceeds u16 length')\n const { source, group } = endpointBytes(endpoint)\n const pseudoheader = new Uint8Array(source.byteLength + group.byteLength + 2 + 2 + 2 + 2 + 4)\n let offset = 0\n pseudoheader.set(source, offset)\n offset += source.byteLength\n pseudoheader.set(group, offset)\n offset += group.byteLength\n pseudoheader.set([0, 0x11], offset)\n offset += 2\n pseudoheader.set(u16(payload.byteLength), offset)\n offset += 2\n pseudoheader.set(u16(endpoint.port), offset)\n offset += 2\n pseudoheader.set(u16(endpoint.port), offset)\n offset += 2\n pseudoheader.set(u32(endpoint.streamId), offset)\n const input = new Uint8Array(pseudoheader.byteLength + payload.byteLength)\n input.set(pseudoheader)\n input.set(payload, pseudoheader.byteLength)\n return sha256(input)\n}\n\n/** Verify the ordered digest list against the transmitted datagrams. */\nexport function verifyBcProvenanceV2Datagrams(\n record: BcProvenanceV2Record,\n endpoint: BcProvenanceV2Endpoint,\n datagrams: readonly Uint8Array[],\n): boolean {\n return record.hashAlg === BCPV2_HASH_ALG_SHA256 &&\n datagrams.length === record.count &&\n datagrams.every((datagram, index) => equalBytes(\n bcProvenanceV2DatagramDigest(endpoint, datagram),\n record.digests[index]!,\n ))\n}\n\nexport interface BcProvenanceV2Params {\n codec: string\n /** 0 where MOQ-MMT Section 4.4.2 resolves none. */\n timescale: number\n groupDurationTicks: number\n /** Absent for an entry with no `fec` object. */\n fec?: {\n algorithm: string\n sourceSymbols: number\n repairSymbols: number\n symbolSize: number\n interleaveDepthMs?: number\n }\n}\n\nfunction concat(parts: Uint8Array[]): Uint8Array {\n const out = new Uint8Array(parts.reduce((n, p) => n + p.byteLength, 0))\n let offset = 0\n for (const part of parts) {\n out.set(part, offset)\n offset += part.byteLength\n }\n return out\n}\n\nfunction lp16(value: Uint8Array | string): Uint8Array {\n const bytes = typeof value === 'string' ? text.encode(value) : value\n if (bytes.byteLength > 0xffff) throw new Error('lp16 field exceeds u16 length')\n return concat([u16(bytes.byteLength), bytes])\n}\n\n/** `params_hash` of multicast-01 Section 7.2.4 (md :1068-1110), hash_alg 1. */\nexport function bcProvenanceV2ParamsHash(params: BcProvenanceV2Params): Uint8Array {\n const fec = params.fec\n return sha256(concat([\n text.encode('BCPV2-params'),\n lp16(params.codec),\n u32(params.timescale),\n u32(params.groupDurationTicks),\n lp16(fec ? fec.algorithm : 'none'),\n u32(fec?.sourceSymbols ?? 0),\n u32(fec?.repairSymbols ?? 0),\n u32(fec?.symbolSize ?? 0),\n u32(fec?.interleaveDepthMs ?? 0),\n ]))\n}\n\n/**\n * `ctx` of multicast-01 Section 7.2.4: the namespace fields in order, the endpoint's\n * packetIds ascending, and `params_hash`. A flow without a MoQ catalog uses 32 zero octets.\n */\nexport function bcProvenanceV2Ctx(\n namespace: readonly (Uint8Array | string)[],\n packetIds: readonly number[],\n params: BcProvenanceV2Params,\n): Uint8Array {\n const ids = [...packetIds].sort((a, b) => a - b)\n if (namespace.length > 0xffff || ids.length > 0xffff) throw new Error('ctx field count exceeds u16')\n return sha256(concat([\n text.encode('BCPV2-ctx'),\n u16(namespace.length),\n ...namespace.map(lp16),\n u16(ids.length),\n ...ids.map(u16),\n bcProvenanceV2ParamsHash(params),\n ]))\n}\n", "/**\n * `multicast.auth` and an endpoint's `auth`: bc-provenance version 2, per\n * draft-ramadan-moq-multicast-01 7.2.6 at\n * Blockcast/moqcast-draft@fd962fd73d21ed281b60219c11ea61cc9e4e91c4.\n *\n * The block lives in the `multicast` object, never the root: the root dotted\n * key `\"multicast.auth\"` was draft -00's (libmmt called its `tracks[]` shape\n * \"multicast.auth v2 (BLO-16700)\", which is the draft's bc-provenance\n * version 1) and is refused. `multicast.auth` declares the profile and its\n * keys; an endpoint's `auth` marks that endpoint's flow protected and carries\n * the flow's parameters. There is no verify-and-report mode (7.2.7): a\n * receiver that implements the profile enforces it.\n *\n * The rules are `multicastCatalogIssues` in `@blockcast/mmt-base`, shared with\n * the transport receivers; this module decodes what they admit.\n */\n\nimport { multicastCatalogIssues } from \"@blockcast/mmt-base\";\nimport { bcProvenanceV2Ctx, type BcProvenanceV2Params, type BcpV2Key } from \"@blockcast/mmt-manifest-verify\";\n\nexport type MulticastAuthScheme = \"bc-provenance\";\nexport type MulticastAuthSig = \"ed25519\";\n\nconst RAW_ED25519_KEY_LENGTH = 32;\n\n/** Default `maxAuthDelayMs` and `manifestTimerMs` of an endpoint's `auth`. */\nexport const DEFAULT_MAX_AUTH_DELAY_MS = 200;\nexport const DEFAULT_MANIFEST_TIMER_MS = 20;\n\nconst BASE64URL_ALPHABET =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n/** Encode raw bytes as unpadded base64url \u2014 the one accepted encoding for\n * `multicast.auth.keys[].publicKey`. */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n\tlet output = \"\";\n\tfor (let i = 0; i < bytes.length; i += 3) {\n\t\tconst b0 = bytes[i]!;\n\t\tconst b1 = bytes[i + 1];\n\t\tconst b2 = bytes[i + 2];\n\t\toutput += BASE64URL_ALPHABET[b0 >> 2];\n\t\toutput += BASE64URL_ALPHABET[((b0 & 0x03) << 4) | (b1 === undefined ? 0 : b1 >> 4)];\n\t\tif (b1 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[((b1 & 0x0f) << 2) | (b2 === undefined ? 0 : b2 >> 6)];\n\t\tif (b2 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[b2 & 0x3f];\n\t}\n\treturn output;\n}\n\nconst BASE64URL_DECODE_TABLE = (() => {\n\tconst table = new Int8Array(128).fill(-1);\n\tfor (let i = 0; i < BASE64URL_ALPHABET.length; i++) {\n\t\ttable[BASE64URL_ALPHABET.charCodeAt(i)] = i;\n\t}\n\treturn table;\n})();\n\n/** Decode unpadded base64url text to raw bytes. Throws on any padding\n * (`=`), standard-alphabet (`+`/`/`), or otherwise invalid character. */\nexport function decodeBase64Url(text: string): Uint8Array {\n\tif (text.length % 4 === 1) {\n\t\tthrow new Error(`[multicast-auth-catalog] invalid unpadded base64url length for \"${text}\"`);\n\t}\n\tconst bytes: number[] = [];\n\tlet buffer = 0;\n\tlet bits = 0;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst code = text.charCodeAt(i);\n\t\tconst value = code < 128 ? BASE64URL_DECODE_TABLE[code] : -1;\n\t\tif (value === undefined || value < 0) {\n\t\t\tthrow new Error(`[multicast-auth-catalog] invalid base64url character in \"${text}\"`);\n\t\t}\n\t\tbuffer = (buffer << 6) | value;\n\t\tbits += 6;\n\t\tif (bits >= 8) {\n\t\t\tbits -= 8;\n\t\t\tbytes.push((buffer >> bits) & 0xff);\n\t\t}\n\t}\n\tconst decoded = Uint8Array.from(bytes);\n\tif (encodeBase64Url(decoded) !== text) {\n\t\tthrow new Error(`[multicast-auth-catalog] non-canonical base64url encoding \"${text}\"`);\n\t}\n\treturn decoded;\n}\n\n/** Decode a catalog key in the only accepted representation: exactly 32 raw\n * ed25519 bytes encoded as canonical, unpadded base64url. */\nexport function decodeEd25519PublicKey(publicKey: string): Uint8Array {\n\tconst bytes = decodeBase64Url(publicKey);\n\tif (bytes.byteLength !== RAW_ED25519_KEY_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`[multicast-auth-catalog] public key must decode to ${RAW_ED25519_KEY_LENGTH} raw bytes, got ${bytes.byteLength}`,\n\t\t);\n\t}\n\treturn bytes;\n}\n\n/** One `multicast.auth.keys[]` entry: `kid` and `firstEpoch` are u32,\n * `publicKey` the 32-octet Ed25519 key in canonical unpadded base64url. */\nexport interface MulticastAuthKey {\n\tkid: number;\n\tpublicKey: string;\n\tfirstEpoch: number;\n}\n\n/** `multicast.auth` (7.2.6). `hash_alg` is a record octet, not a member. */\nexport interface MulticastAuthConfig {\n\tscheme: MulticastAuthScheme;\n\tversion: 2;\n\tsig: MulticastAuthSig;\n\t/** One to four keys; `kid` and `firstEpoch` each distinct. */\n\tkeys: MulticastAuthKey[];\n\t/** Lowest `epoch` a receiver accepts at join. */\n\tminEpoch: number;\n}\n\n/** An endpoint's `auth` (7.2.6). A protected endpoint also declares `bandwidth`. */\nexport interface EndpointAuthConfig {\n\t/** Destination port of the record datagrams on the endpoint's `(S, G)`. */\n\tmanifestPort: number;\n\t/** u32 chosen per channel generation. */\n\tstreamId: number;\n\t/** `S` of a protected ASM endpoint; never on SSM. */\n\tsenderAddress?: string;\n\t/** 50..1000, default 200. */\n\tmaxAuthDelayMs?: number;\n\t/** At least 1, default 20. */\n\tmanifestTimerMs?: number;\n\t/** MoQ track mirroring the records. */\n\tmanifestTrack?: string;\n}\n\n/** Structural check of a `multicast.auth` block, duplicate `kid` and\n * `firstEpoch` included. */\nexport function isMulticastAuthConfig(obj: unknown): obj is MulticastAuthConfig {\n\treturn multicastCatalogIssues({ multicast: { auth: obj } }).length === 0;\n}\n\n/** A protected flow as a receiver binds it: one per protected endpoint. */\nexport interface ResolvedMulticastAuthFlow {\n\t/** Index of the endpoint in `multicast.endpoints`. */\n\tendpointIndex: number;\n\t/** `S`: `sourceAddress` on SSM, `auth.senderAddress` on ASM. */\n\tsource: string;\n\tgroupAddress: string;\n\tport: number;\n\tbandwidth: number;\n\tmanifestPort: number;\n\tstreamId: number;\n\tmaxAuthDelayMs: number;\n\tmanifestTimerMs: number;\n\tmanifestTrack?: string;\n\ttracks: Array<{ name: string; packetId: number }>;\n\t/** `ctx` recomputed from the catalog (7.2.4); present when `namespaceFields` was given. */\n\tctx?: Uint8Array;\n}\n\nexport interface ResolvedMulticastAuthConfig {\n\t/** Decoded keys, ascending by `firstEpoch`. */\n\tkeys: BcpV2Key[];\n\tminEpoch: number;\n\tflows: ResolvedMulticastAuthFlow[];\n}\n\n/**\n * Resolve `multicast.auth` and every protected endpoint into decoded keys and\n * per-endpoint flows. Returns `undefined` for an unprotected catalog unless\n * `requireAuth` pins authentication (7.2.7), which then throws.\n *\n * A catalog-level fault (no key set: a malformed block, a repeated `kid` or\n * `firstEpoch`, the retired root key) throws, so nothing is admitted and\n * nothing falls back to unauthenticated reception. An endpoint the rules make\n * a receiver ignore yields no flow; the others still bind.\n *\n * `namespaceFields` are the Track Namespace Fields of the catalog track, as\n * MOQT carries them. With them every flow carries the recomputed `ctx`, which\n * the receiver compares against each record; without them `ctx` is carried\n * blind (7.2.4).\n */\nexport function resolveMulticastAuthCatalog(\n\tcatalog: unknown,\n\toptions: {\n\t\trequireAuth?: boolean;\n\t\tcatalogNamespace?: string;\n\t\tnamespaceFields?: readonly (string | Uint8Array)[];\n\t} = {},\n): ResolvedMulticastAuthConfig | undefined {\n\tif (typeof catalog !== \"object\" || catalog === null || Array.isArray(catalog)) {\n\t\tthrow new Error(\"[multicast-auth-catalog] catalog must be an object\");\n\t}\n\tconst issues = multicastCatalogIssues(catalog, options);\n\tconst fatal = issues.find((issue) => issue.endpointIndex === undefined);\n\tif (fatal) throw new Error(`[multicast-auth-catalog] ${fatal.path}: ${fatal.message}`);\n\tconst multicast = (catalog as Record<string, unknown>)[\"multicast\"] as Record<string, unknown> | undefined;\n\tconst auth = multicast?.[\"auth\"] as MulticastAuthConfig | undefined;\n\tif (auth === undefined) {\n\t\tif (options.requireAuth) {\n\t\t\tthrow new Error(\"[multicast-auth-catalog] authentication is pinned but the catalog declares no multicast.auth\");\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst ignored = new Set(issues.map((issue) => issue.endpointIndex));\n\tconst endpoints = (multicast![\"endpoints\"] ?? []) as Array<Record<string, unknown>>;\n\tconst catalogTracks = ((catalog as Record<string, unknown>)[\"tracks\"] ?? []) as Array<Record<string, unknown>>;\n\tconst flows: ResolvedMulticastAuthFlow[] = [];\n\tendpoints.forEach((endpoint, endpointIndex) => {\n\t\tif (ignored.has(endpointIndex)) return;\n\t\tconst flowAuth = endpoint[\"auth\"] as EndpointAuthConfig;\n\t\tconst tracks = ((endpoint[\"tracks\"] ?? []) as Array<{ name: string; packetId: number }>).map(({ name, packetId }) => ({\n\t\t\tname,\n\t\t\tpacketId,\n\t\t}));\n\t\tlet ctx: Uint8Array | undefined;\n\t\tif (options.namespaceFields !== undefined) {\n\t\t\tconst params = ctxParams(catalogTracks, tracks);\n\t\t\t// No catalog entry to hash: the endpoint is ignored, nothing is read unauthenticated.\n\t\t\tif (params === undefined) return;\n\t\t\tctx = bcProvenanceV2Ctx(options.namespaceFields, tracks.map((track) => track.packetId), params);\n\t\t}\n\t\tflows.push({\n\t\t\tendpointIndex,\n\t\t\tsource: (endpoint[\"sourceAddress\"] ?? flowAuth.senderAddress) as string,\n\t\t\tgroupAddress: endpoint[\"groupAddress\"] as string,\n\t\t\tport: endpoint[\"port\"] as number,\n\t\t\tbandwidth: endpoint[\"bandwidth\"] as number,\n\t\t\tmanifestPort: flowAuth.manifestPort,\n\t\t\tstreamId: flowAuth.streamId,\n\t\t\tmaxAuthDelayMs: flowAuth.maxAuthDelayMs ?? DEFAULT_MAX_AUTH_DELAY_MS,\n\t\t\tmanifestTimerMs: flowAuth.manifestTimerMs ?? DEFAULT_MANIFEST_TIMER_MS,\n\t\t\t...(flowAuth.manifestTrack === undefined ? {} : { manifestTrack: flowAuth.manifestTrack }),\n\t\t\ttracks,\n\t\t\t...(ctx === undefined ? {} : { ctx }),\n\t\t});\n\t});\n\treturn {\n\t\tkeys: auth.keys\n\t\t\t.map((key) => ({ kid: key.kid, publicKey: decodeEd25519PublicKey(key.publicKey), firstEpoch: key.firstEpoch }))\n\t\t\t.sort((a, b) => a.firstEpoch - b.firstEpoch),\n\t\tminEpoch: auth.minEpoch,\n\t\tflows,\n\t};\n}\n\n/** A positive integer that fits the u32 it is hashed as (7.2.4), else `0`. */\nfunction positiveInteger(value: unknown): number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 && value <= 0xffffffff ? value : 0;\n}\n\n/**\n * `params_hash` inputs (7.2.4): the catalog entry of the endpoint's\n * lowest-`packetId` media track or, for a repair-only endpoint, of the source\n * its lowest-`packetId` repair track repairs (`depends`, else the track whose\n * `fec.repairTrack` names it). A timescale or group duration that does not\n * resolve to an integer tick count is `0`.\n */\nfunction ctxParams(\n\tcatalogTracks: Array<Record<string, unknown>>,\n\tlisted: Array<{ name: string; packetId: number }>,\n): BcProvenanceV2Params | undefined {\n\tconst byName = new Map(catalogTracks.map((track) => [track[\"name\"], track]));\n\tconst ordered = [...listed].sort((a, b) => a.packetId - b.packetId).map(({ name }) => byName.get(name));\n\tlet entry = ordered.find((track) => track !== undefined && track[\"packaging\"] !== \"fec-repair\");\n\tif (entry === undefined) {\n\t\tconst repair = ordered.find((track) => track !== undefined);\n\t\tif (repair === undefined) return undefined;\n\t\tconst depends = repair[\"depends\"];\n\t\tentry = Array.isArray(depends) && depends.length === 1\n\t\t\t? byName.get(depends[0])\n\t\t\t: catalogTracks.find((track) => (track[\"fec\"] as Record<string, unknown> | undefined)?.[\"repairTrack\"] === repair[\"name\"]);\n\t\tif (entry === undefined) return undefined;\n\t}\n\tconst timescale = positiveInteger(entry[\"timescale\"]);\n\tconst ms = positiveInteger(entry[\"groupDurationMs\"]);\n\tconst groupDurationTicks = positiveInteger(entry[\"groupDurationTicks\"]) ||\n\t\tpositiveInteger(timescale && (ms * timescale) % 1000 === 0 ? (ms * timescale) / 1000 : 0);\n\tconst fec = entry[\"fec\"] as Record<string, unknown> | undefined;\n\treturn {\n\t\tcodec: typeof entry[\"codec\"] === \"string\" ? entry[\"codec\"] : \"\",\n\t\ttimescale,\n\t\tgroupDurationTicks,\n\t\t...(fec === undefined || fec === null ? {} : {\n\t\t\tfec: {\n\t\t\t\talgorithm: typeof fec[\"algorithm\"] === \"string\" ? fec[\"algorithm\"] : \"\",\n\t\t\t\tsourceSymbols: positiveInteger(fec[\"sourceSymbols\"]),\n\t\t\t\trepairSymbols: positiveInteger(fec[\"repairSymbols\"]),\n\t\t\t\tsymbolSize: positiveInteger(fec[\"symbolSize\"]),\n\t\t\t\tinterleaveDepthMs: positiveInteger(fec[\"interleaveDepthMs\"]),\n\t\t\t},\n\t\t}),\n\t};\n}\n\n/** The key that signs a record of `kid` at `epoch`, or `undefined` when that\n * `kid` does not sign it (7.2.7: such a record is dropped before verification). */\nexport function multicastAuthKeyFor(\n\tresolved: ResolvedMulticastAuthConfig,\n\tkid: number,\n\tepoch: number,\n): Uint8Array | undefined {\n\t// `keys` is ascending by firstEpoch with unique kid and firstEpoch (resolve throws otherwise).\n\tconst signers = resolved.keys.filter((key) => key.firstEpoch <= epoch);\n\tconst signer = signers[signers.length - 1];\n\treturn signer?.kid === kid ? signer.publicKey : undefined;\n}\n"],
|
|
5
|
-
"mappings": ";AAsEO,IAAM,kBAAkB;AAI/B,IAAM,wBAAwB;AAC9B,IAAM,4BAA4B,kBAAkB;;;AC9D7C,IAAM,iBAAiB;AAGvB,IAAM,oBAAoB,KAAK;AAQ/B,IAAM,uBAAuB,MAAM,iBAAiB;;;ACSpD,IAAM,qBAAiD,OAAO,OAAO;EAC1E;CACD;;;ACGM,IAAM,+BAA+B;AAE5C,IAAM,aAAa;AACnB,IAAM,eAAe,oBAAI,IAAI,CAAC,UAAU,WAAW,OAAO,QAAQ,UAAU,CAAC;AAC7E,IAAM,cAAc,oBAAI,IAAI,CAAC,OAAO,aAAa,YAAY,CAAC;AAC9D,IAAM,wBAAwB,oBAAI,IAAI;EACrC;EACA;EACA;EACA;EACA;EACA;CACA;AAID,SAAS,SAAS,OAAc;AAC/B,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC3E;AAEA,SAAS,iBAAiB,OAAc;AACvC,SAAO,OAAO,UAAU,YAAY,MAAM,KAAI,EAAG,SAAS;AAC3D;AAEA,SAAS,YAAY,OAAgB,KAAa,KAAW;AAC5D,SAAO,OAAO,cAAc,KAAK,KAAM,SAAoB,OAAQ,SAAoB;AACxF;AAQM,SAAU,wBAAwB,UAAiB;AACxD,MAAI,CAAC,SAAS,QAAQ;AAAG,WAAO;AAChC,QAAM,gBAAgB,SAAS,eAAe;AAC9C,MAAI,kBAAkB,UAAa,CAAC,iBAAiB,aAAa;AAAG,WAAO;AAC5E,QAAM,OAA0B,kBAAkB,SAAY,QAAQ;AACtE,QAAM,WAAW,SAAS,UAAU;AACpC,MAAI,aAAa,UAAa,aAAa;AAAM,WAAO;AACxD,QAAM,OAAO,SAAS,MAAM;AAC5B,QAAM,gBAAgB,SAAS,IAAI,IAAI,KAAK,eAAe,IAAI;AAC/D,MAAI,SAAS;AAAO,WAAO,kBAAkB,SAAY,EAAE,MAAM,QAAQ,cAAuB,IAAK;AACrG,MAAI,SAAS;AAAW,WAAO,EAAE,KAAI;AACrC,SAAO,iBAAiB,aAAa,IAAI,EAAE,MAAM,QAAQ,cAAa,IAAK;AAC5E;AAEA,SAAS,WAAW,MAAa;AAChC,QAAM,OAAO;AACb,QAAM,QAAQ,CAAC,MAAc,YAA6C;IACzE,EAAE,MAAM,kBAAkB,MAAM,GAAG,IAAI,GAAG,IAAI,IAAI,QAAO;;AAE1D,MAAI,CAAC,SAAS,IAAI;AAAG,WAAO,MAAM,IAAI,wBAAwB;AAC9D,QAAM,UAAU,OAAO,KAAK,IAAI,EAAE,OAAO,SAAO,CAAC,aAAa,IAAI,GAAG,CAAC;AACtE,MAAI,QAAQ,SAAS,GAAG;AACvB,WAAO,MAAM,IAAI,8BAA8B,QAAQ,KAAK,IAAI,CAAC,mEAAmE;EACrI;AACA,MAAI,KAAK,QAAQ,MAAM;AAAiB,WAAO,MAAM,WAAW,gCAAgC;AAChG,MAAI,KAAK,SAAS,MAAM;AAAG,WAAO,MAAM,YAAY,4DAA4D;AAChH,MAAI,KAAK,KAAK,MAAM;AAAW,WAAO,MAAM,QAAQ,uBAAuB;AAC3E,QAAM,OAAO,KAAK,MAAM;AACxB,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,SAAS,KAAK,KAAK,SAAS,GAAG;AAC/D,WAAO,MAAM,SAAS,2EAA2E;EAClG;AACA,QAAM,OAAO,oBAAI,IAAG;AACpB,QAAM,cAAc,oBAAI,IAAG;AAC3B,WAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS;AACjD,UAAM,MAAM,KAAK,KAAK;AACtB,UAAM,KAAK,SAAS,KAAK;AACzB,QAAI,CAAC,SAAS,GAAG,KAAK,OAAO,KAAK,GAAG,EAAE,KAAK,YAAU,CAAC,YAAY,IAAI,MAAM,CAAC,GAAG;AAChF,aAAO,MAAM,IAAI,+CAA+C;IACjE;AACA,QAAI,CAAC,YAAY,IAAI,KAAK,GAAG,GAAG,UAAU;AAAG,aAAO,MAAM,GAAG,EAAE,QAAQ,wCAAwC;AAC/G,QAAI,OAAO,IAAI,WAAW,MAAM,YAAY,CAAC,6BAA6B,KAAK,IAAI,WAAW,CAAC,GAAG;AACjG,aAAO,MAAM,GAAG,EAAE,cAAc,0EAA0E;IAC3G;AACA,QAAI,CAAC,YAAY,IAAI,YAAY,GAAG,GAAG,UAAU,GAAG;AACnD,aAAO,MAAM,GAAG,EAAE,eAAe,+CAA+C;IACjF;AAGA,QAAI,KAAK,IAAI,IAAI,KAAK,CAAC;AAAG,aAAO,MAAM,GAAG,EAAE,QAAQ,OAAO,IAAI,KAAK,CAAC,4CAA4C;AACjH,QAAI,YAAY,IAAI,IAAI,YAAY,CAAC,GAAG;AACvC,aAAO,MAAM,GAAG,EAAE,eAAe,cAAc,IAAI,YAAY,CAAC,4CAA4C;IAC7G;AACA,SAAK,IAAI,IAAI,KAAK,CAAC;AACnB,gBAAY,IAAI,IAAI,YAAY,CAAC;EAClC;AACA,MAAI,CAAC,YAAY,KAAK,UAAU,GAAG,GAAG,UAAU,GAAG;AAClD,WAAO,MAAM,aAAa,6CAA6C;EACxE;AACA,SAAO,CAAA;AACR;AAEA,SAAS,kBACR,UACA,MACA,IAAU;AAEV,QAAM,OAAO,SAAS,MAAM;AAC5B,MAAI,CAAC,SAAS,IAAI;AAAG,WAAO,EAAE,MAAM,GAAG,EAAE,SAAS,SAAS,yBAAwB;AACnF,QAAM,UAAU,OAAO,KAAK,IAAI,EAAE,OAAO,SAAO,CAAC,sBAAsB,IAAI,GAAG,CAAC;AAC/E,MAAI,QAAQ,SAAS;AAAG,WAAO,EAAE,MAAM,GAAG,EAAE,SAAS,SAAS,8BAA8B,QAAQ,KAAK,IAAI,CAAC,GAAE;AAChH,MAAI,CAAC,YAAY,KAAK,cAAc,GAAG,GAAG,KAAK,GAAG;AACjD,WAAO,EAAE,MAAM,GAAG,EAAE,sBAAsB,SAAS,kDAAiD;EACrG;AACA,MAAI,CAAC,YAAY,KAAK,UAAU,GAAG,GAAG,UAAU,GAAG;AAClD,WAAO,EAAE,MAAM,GAAG,EAAE,kBAAkB,SAAS,8CAA6C;EAC7F;AACA,MAAI,SAAS,SAAS,KAAK,eAAe,MAAM,QAAW;AAC1D,WAAO,EAAE,MAAM,GAAG,EAAE,uBAAuB,SAAS,uEAAsE;EAC3H;AACA,MAAI,SAAS,SAAS,CAAC,iBAAiB,KAAK,eAAe,CAAC,GAAG;AAC/D,WAAO,EAAE,MAAM,GAAG,EAAE,uBAAuB,SAAS,kDAAiD;EACtG;AACA,MAAI,KAAK,gBAAgB,MAAM,UAAa,CAAC,YAAY,KAAK,gBAAgB,GAAG,IAAI,GAAI,GAAG;AAC3F,WAAO,EAAE,MAAM,GAAG,EAAE,wBAAwB,SAAS,oDAAmD;EACzG;AACA,MAAI,KAAK,iBAAiB,MAAM,UAAa,CAAC,YAAY,KAAK,iBAAiB,GAAG,GAAG,OAAO,gBAAgB,GAAG;AAC/G,WAAO,EAAE,MAAM,GAAG,EAAE,yBAAyB,SAAS,mDAAkD;EACzG;AACA,MAAI,KAAK,eAAe,MAAM,UAAa,CAAC,iBAAiB,KAAK,eAAe,CAAC,GAAG;AACpF,WAAO,EAAE,MAAM,GAAG,EAAE,uBAAuB,SAAS,2CAA0C;EAC/F;AACA,MAAI,CAAC,YAAY,SAAS,WAAW,GAAG,GAAG,OAAO,gBAAgB,GAAG;AACpE,WAAO,EAAE,MAAM,GAAG,EAAE,cAAc,SAAS,mFAAkF;EAC9H;AACA,SAAO;AACR;AAOO,IAAM,oBAAmC,uBAAO,IAAI,sCAAsC;AAyB3F,SAAU,uBACf,SACA,UAAyC,CAAA,GAAE;AAE3C,MAAI,CAAC,SAAS,OAAO;AAAG,WAAO,CAAA;AAC/B,QAAM,SAAkC,CAAA;AACxC,MAAI,oBAAoB,SAAS;AAGhC,WAAO,KAAK;MACX,MAAM;MACN,MAAM;MACN,SAAS;KACT;EACF;AACA,QAAM,YAAY,QAAQ,WAAW;AACrC,MAAI,CAAC,SAAS,SAAS;AAAG,WAAO;AACjC,QAAM,mBAAmB,UAAU,MAAM,MAAM;AAC/C,MAAI;AAAkB,WAAO,KAAK,GAAG,WAAW,UAAU,MAAM,CAAC,CAAC;AAClE,QAAM,YAAY,UAAU,WAAW;AACvC,MAAI,CAAC,MAAM,QAAQ,SAAS;AAAG,WAAO;AAEtC,QAAM,SAAS,MAAM,QAAQ,QAAQ,QAAQ,CAAC,IAAI,QAAQ,QAAQ,IAAI,CAAA;AACtE,QAAM,cAAc,oBAAI,IAAG;AAC3B,aAAW,SAAS,QAAQ;AAC3B,QAAI,SAAS,KAAK,KAAK,OAAO,MAAM,MAAM,MAAM;AAAU,kBAAY,IAAI,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;EAC5G;AACA,QAAM,SAAU,QAA8C,iBAAiB;AAC/E,MAAI,qBAAqB,QAAQ,qBAAqB,OAAO,WAAW,WAAW,SAAS;AAE5F,QAAM,UAAsD,CAAA;AAC5D,WAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;AACtD,UAAM,WAAW,UAAU,KAAK;AAChC,UAAM,KAAK,yBAAyB,KAAK;AACzC,UAAM,OAAO,CAAC,MAAiC,MAAc,YAAmB;AAC/E,aAAO,KAAK,EAAE,MAAM,MAAM,SAAS,eAAe,MAAK,CAAE;IAC1D;AACA,YAAQ,KAAK,MAAS;AACtB,QAAI,CAAC,SAAS,QAAQ,GAAG;AACxB,WAAK,sBAAsB,IAAI,4BAA4B;AAC3D;IACD;AACA,UAAM,WAAW,SAAS,UAAU;AACpC,QAAI,aAAa,UAAa,aAAa,SAAS,aAAa,OAAO;AACvE,WAAK,sBAAsB,GAAG,EAAE,aAAa,qDAAqD,KAAK,UAAU,QAAQ,CAAC,2CAA2C;AACrK;IACD;AACA,UAAM,UAAU,SAAS,MAAM,MAAM;AACrC,QAAI,qBAAqB,SAAS;AACjC,WACC,2BACA,GAAG,EAAE,SACL,mBACG,+FACA,4EAA4E;AAEhF;IACD;AACA,UAAM,gBAAgB,SAAS,eAAe;AAC9C,QAAI,kBAAkB,UAAa,CAAC,iBAAiB,aAAa,GAAG;AACpE,WAAK,sBAAsB,GAAG,EAAE,kBAAkB,0CAA0C;AAC5F;IACD;AACA,UAAM,OAA0B,kBAAkB,SAAY,QAAQ;AACtE,QAAI,aAAa,UAAa,aAAa,MAAM;AAChD,WAAK,sBAAsB,GAAG,EAAE,aAAa,YAAY,KAAK,UAAU,QAAQ,CAAC,gDAAgD,IAAI,EAAE;AACvI;IACD;AAGA,QAAI,CAAC,YAAY,SAAS,MAAM,GAAG,GAAG,KAAK,GAAG;AAC7C,WAAK,sBAAsB,GAAG,EAAE,SAAS,yCAAyC;AAClF;IACD;AACA,QAAI,SAAS;AACZ,YAAM,YAAY,kBAAkB,UAAU,MAAM,EAAE;AACtD,UAAI,WAAW;AACd,aAAK,2BAA2B,UAAU,MAAM,UAAU,OAAO;AACjE;MACD;IACD;AACA,YAAQ,KAAK,IAAI,wBAAwB,QAAQ;AAEjD,UAAM,OAAO,MAAM,QAAQ,SAAS,QAAQ,CAAC,IAAI,SAAS,QAAQ,IAAI,CAAA;AACtE,aAAS,WAAW,GAAG,WAAW,KAAK,QAAQ,YAAY;AAC1D,YAAM,MAAM,KAAK,QAAQ;AACzB,YAAM,OAAO,SAAS,GAAG,IAAI,IAAI,MAAM,IAAI;AAC3C,UAAI,OAAO,SAAS;AAAU;AAC9B,YAAM,YAAY,YAAY,IAAI,IAAI;AACtC,UAAI,OAAO,cAAc;AAAU;AACnC,6BAAuB;AACvB,UAAI,cAAc,oBAAoB;AACrC,aAAK,mBAAmB,GAAG,EAAE,WAAW,QAAQ,UAAU,SAAS,KAAK,UAAU,IAAI,CAAC,uBAAuB,KAAK,UAAU,SAAS,CAAC,6BAA6B,KAAK,UAAU,kBAAkB,CAAC,EAAE;MACzM;IACD;EACD;AAMA,QAAM,UAAU,CAAC,UAAiB;AACjC,UAAM,WAAW,UAAU,KAAK;AAChC,UAAM,OAAO,SAAS,MAAM;AAC5B,WAAO;MACN,IAAI,KAAK,UAAU,CAAC,QAAQ,KAAK,EAAG,UAAU,MAAM,SAAS,cAAc,CAAC,CAAC;MAC7E,MAAM,SAAS,MAAM;MACrB,cAAc,SAAS,IAAI,IAAI,KAAK,cAAc,IAAI;;EAExD;AACA,WAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;AACtD,QAAI,CAAC,QAAQ,KAAK;AAAG;AACrB,UAAM,MAAM,QAAQ,KAAK;AACzB,UAAM,KAAK,yBAAyB,KAAK;AACzC,QAAI,IAAI,iBAAiB,UAAa,IAAI,iBAAiB,IAAI,MAAM;AACpE,aAAO,KAAK,EAAE,MAAM,2BAA2B,MAAM,GAAG,EAAE,sBAAsB,SAAS,yDAAyD,eAAe,MAAK,CAAE;AACxK;IACD;AACA,aAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS;AACtD,UAAI,UAAU,SAAS,CAAC,QAAQ,KAAK;AAAG;AACxC,YAAM,OAAO,QAAQ,KAAK;AAC1B,UAAI,KAAK,OAAO,IAAI;AAAI;AACxB,UAAI,KAAK,SAAS,IAAI,MAAM;AAC3B,eAAO,KAAK,EAAE,MAAM,sBAAsB,MAAM,GAAG,EAAE,SAAS,SAAS,wCAAwC,KAAK,6CAA6C,eAAe,MAAK,CAAE;AACvL;MACD;AACA,UAAI,IAAI,iBAAiB,WAAc,IAAI,iBAAiB,KAAK,QAAQ,IAAI,iBAAiB,KAAK,eAAe;AACjH,eAAO,KAAK,EAAE,MAAM,2BAA2B,MAAM,GAAG,EAAE,sBAAsB,SAAS,sDAAsD,KAAK,uBAAuB,eAAe,MAAK,CAAE;AACjM;MACD;IACD;EACD;AACA,SAAO;AACR;;;AClTO,IAAM,uBAA2C,OAAO,OAAO;EACpE,SAAS,OAAO,OAAO,CAAC,WAAW,cAAc,CAAC;EAClD,eAAe,OAAO,OAAO,CAAC,cAAc,CAAC;EAC7C,UAAU;CACX;;;ACfK,SAAU,QAAQ,GAAU;AAChC,SAAO,aAAa,cAAe,YAAY,OAAO,CAAC,KAAK,EAAE,YAAY,SAAS;AACrF;AAQM,SAAU,OAAO,MAA8B,SAAiB;AACpE,MAAI,CAAC,QAAQ,CAAC;AAAG,UAAM,IAAI,MAAM,qBAAqB;AACtD,MAAI,QAAQ,SAAS,KAAK,CAAC,QAAQ,SAAS,EAAE,MAAM;AAClD,UAAM,IAAI,MAAM,mCAAmC,UAAU,kBAAkB,EAAE,MAAM;AAC3F;AAWM,SAAU,QAAQ,UAAe,gBAAgB,MAAI;AACzD,MAAI,SAAS;AAAW,UAAM,IAAI,MAAM,kCAAkC;AAC1E,MAAI,iBAAiB,SAAS;AAAU,UAAM,IAAI,MAAM,uCAAuC;AACjG;AAGM,SAAU,QAAQ,KAAU,UAAa;AAC7C,SAAO,GAAG;AACV,QAAM,MAAM,SAAS;AACrB,MAAI,IAAI,SAAS,KAAK;AACpB,UAAM,IAAI,MAAM,2DAA2D,GAAG;EAChF;AACF;AAkBM,SAAU,SAAS,QAAoB;AAC3C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,WAAO,CAAC,EAAE,KAAK,CAAC;EAClB;AACF;AAGM,SAAU,WAAW,KAAe;AACxC,SAAO,IAAI,SAAS,IAAI,QAAQ,IAAI,YAAY,IAAI,UAAU;AAChE;AAGM,SAAU,KAAK,MAAc,OAAa;AAC9C,SAAQ,QAAS,KAAK,QAAW,SAAS;AAC5C;AAkIM,SAAU,YAAY,KAAW;AACrC,MAAI,OAAO,QAAQ;AAAU,UAAM,IAAI,MAAM,iBAAiB;AAC9D,SAAO,IAAI,WAAW,IAAI,YAAW,EAAG,OAAO,GAAG,CAAC;AACrD;AAiBM,SAAU,QAAQ,MAAW;AACjC,MAAI,OAAO,SAAS;AAAU,WAAO,YAAY,IAAI;AACrD,SAAO,IAAI;AACX,SAAO;AACT;AAmDM,IAAgB,OAAhB,MAAoB;;AA4CpB,SAAU,aACd,UAAuB;AAOvB,QAAM,QAAQ,CAAC,QAA2B,SAAQ,EAAG,OAAO,QAAQ,GAAG,CAAC,EAAE,OAAM;AAChF,QAAM,MAAM,SAAQ;AACpB,QAAM,YAAY,IAAI;AACtB,QAAM,WAAW,IAAI;AACrB,QAAM,SAAS,MAAM,SAAQ;AAC7B,SAAO;AACT;;;ACpVM,SAAU,aACd,MACA,YACA,OACA,MAAa;AAEb,MAAI,OAAO,KAAK,iBAAiB;AAAY,WAAO,KAAK,aAAa,YAAY,OAAO,IAAI;AAC7F,QAAM,OAAO,OAAO,EAAE;AACtB,QAAM,WAAW,OAAO,UAAU;AAClC,QAAM,KAAK,OAAQ,SAAS,OAAQ,QAAQ;AAC5C,QAAM,KAAK,OAAO,QAAQ,QAAQ;AAClC,QAAM,IAAI,OAAO,IAAI;AACrB,QAAM,IAAI,OAAO,IAAI;AACrB,OAAK,UAAU,aAAa,GAAG,IAAI,IAAI;AACvC,OAAK,UAAU,aAAa,GAAG,IAAI,IAAI;AACzC;AAGM,SAAU,IAAI,GAAW,GAAW,GAAS;AACjD,SAAQ,IAAI,IAAM,CAAC,IAAI;AACzB;AAGM,SAAU,IAAI,GAAW,GAAW,GAAS;AACjD,SAAQ,IAAI,IAAM,IAAI,IAAM,IAAI;AAClC;AAMM,IAAgB,SAAhB,cAAoD,KAAO;EAoB/D,YAAY,UAAkB,WAAmB,WAAmB,MAAa;AAC/E,UAAK;AANG,SAAA,WAAW;AACX,SAAA,SAAS;AACT,SAAA,MAAM;AACN,SAAA,YAAY;AAIpB,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY;AACjB,SAAK,OAAO;AACZ,SAAK,SAAS,IAAI,WAAW,QAAQ;AACrC,SAAK,OAAO,WAAW,KAAK,MAAM;EACpC;EACA,OAAO,MAAW;AAChB,YAAQ,IAAI;AACZ,WAAO,QAAQ,IAAI;AACnB,WAAO,IAAI;AACX,UAAM,EAAE,MAAM,QAAQ,SAAQ,IAAK;AACnC,UAAM,MAAM,KAAK;AACjB,aAAS,MAAM,GAAG,MAAM,OAAO;AAC7B,YAAM,OAAO,KAAK,IAAI,WAAW,KAAK,KAAK,MAAM,GAAG;AAEpD,UAAI,SAAS,UAAU;AACrB,cAAM,WAAW,WAAW,IAAI;AAChC,eAAO,YAAY,MAAM,KAAK,OAAO;AAAU,eAAK,QAAQ,UAAU,GAAG;AACzE;MACF;AACA,aAAO,IAAI,KAAK,SAAS,KAAK,MAAM,IAAI,GAAG,KAAK,GAAG;AACnD,WAAK,OAAO;AACZ,aAAO;AACP,UAAI,KAAK,QAAQ,UAAU;AACzB,aAAK,QAAQ,MAAM,CAAC;AACpB,aAAK,MAAM;MACb;IACF;AACA,SAAK,UAAU,KAAK;AACpB,SAAK,WAAU;AACf,WAAO;EACT;EACA,WAAW,KAAe;AACxB,YAAQ,IAAI;AACZ,YAAQ,KAAK,IAAI;AACjB,SAAK,WAAW;AAIhB,UAAM,EAAE,QAAQ,MAAM,UAAU,KAAI,IAAK;AACzC,QAAI,EAAE,IAAG,IAAK;AAEd,WAAO,KAAK,IAAI;AAChB,UAAM,KAAK,OAAO,SAAS,GAAG,CAAC;AAG/B,QAAI,KAAK,YAAY,WAAW,KAAK;AACnC,WAAK,QAAQ,MAAM,CAAC;AACpB,YAAM;IACR;AAEA,aAAS,IAAI,KAAK,IAAI,UAAU;AAAK,aAAO,CAAC,IAAI;AAIjD,iBAAa,MAAM,WAAW,GAAG,OAAO,KAAK,SAAS,CAAC,GAAG,IAAI;AAC9D,SAAK,QAAQ,MAAM,CAAC;AACpB,UAAM,QAAQ,WAAW,GAAG;AAC5B,UAAM,MAAM,KAAK;AAEjB,QAAI,MAAM;AAAG,YAAM,IAAI,MAAM,6CAA6C;AAC1E,UAAM,SAAS,MAAM;AACrB,UAAM,QAAQ,KAAK,IAAG;AACtB,QAAI,SAAS,MAAM;AAAQ,YAAM,IAAI,MAAM,oCAAoC;AAC/E,aAAS,IAAI,GAAG,IAAI,QAAQ;AAAK,YAAM,UAAU,IAAI,GAAG,MAAM,CAAC,GAAG,IAAI;EACxE;EACA,SAAM;AACJ,UAAM,EAAE,QAAQ,UAAS,IAAK;AAC9B,SAAK,WAAW,MAAM;AACtB,UAAM,MAAM,OAAO,MAAM,GAAG,SAAS;AACrC,SAAK,QAAO;AACZ,WAAO;EACT;EACA,WAAW,IAAM;AACf,WAAA,KAAO,IAAK,KAAK,YAAmB;AACpC,OAAG,IAAI,GAAG,KAAK,IAAG,CAAE;AACpB,UAAM,EAAE,UAAU,QAAQ,QAAQ,UAAU,WAAW,IAAG,IAAK;AAC/D,OAAG,YAAY;AACf,OAAG,WAAW;AACd,OAAG,SAAS;AACZ,OAAG,MAAM;AACT,QAAI,SAAS;AAAU,SAAG,OAAO,IAAI,MAAM;AAC3C,WAAO;EACT;EACA,QAAK;AACH,WAAO,KAAK,WAAU;EACxB;;AASK,IAAM,YAAyC,4BAAY,KAAK;EACrE;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;CACrF;;;AC9ID,IAAM,WAA2B,4BAAY,KAAK;EAChD;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EACpF;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;EAAY;CACrF;AAGD,IAAM,WAA2B,oBAAI,YAAY,EAAE;AAC7C,IAAO,SAAP,cAAsB,OAAc;EAYxC,YAAY,YAAoB,IAAE;AAChC,UAAM,IAAI,WAAW,GAAG,KAAK;AAVrB,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;AAC3B,SAAA,IAAY,UAAU,CAAC,IAAI;EAIrC;EACU,MAAG;AACX,UAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAC,IAAK;AACnC,WAAO,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;EAChC;;EAEU,IACR,GAAW,GAAW,GAAW,GAAW,GAAW,GAAW,GAAW,GAAS;AAEtF,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;AACb,SAAK,IAAI,IAAI;EACf;EACU,QAAQ,MAAgB,QAAc;AAE9C,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK,UAAU;AAAG,eAAS,CAAC,IAAI,KAAK,UAAU,QAAQ,KAAK;AACpF,aAAS,IAAI,IAAI,IAAI,IAAI,KAAK;AAC5B,YAAM,MAAM,SAAS,IAAI,EAAE;AAC3B,YAAM,KAAK,SAAS,IAAI,CAAC;AACzB,YAAM,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,IAAK,QAAQ;AACnD,YAAM,KAAK,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAK,OAAO;AACjD,eAAS,CAAC,IAAK,KAAK,SAAS,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAK;IACjE;AAEA,QAAI,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,EAAC,IAAK;AACjC,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,YAAM,SAAS,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE;AACpD,YAAM,KAAM,IAAI,SAAS,IAAI,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,SAAS,CAAC,IAAK;AACrE,YAAM,SAAS,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,EAAE,IAAI,KAAK,GAAG,EAAE;AACpD,YAAM,KAAM,SAAS,IAAI,GAAG,GAAG,CAAC,IAAK;AACrC,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAK,IAAI,KAAM;AACf,UAAI;AACJ,UAAI;AACJ,UAAI;AACJ,UAAK,KAAK,KAAM;IAClB;AAEA,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,QAAK,IAAI,KAAK,IAAK;AACnB,SAAK,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;EACjC;EACU,aAAU;AAClB,UAAM,QAAQ;EAChB;EACA,UAAO;AACL,SAAK,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAC/B,UAAM,KAAK,MAAM;EACnB;;AAuRK,IAAM,SAAgC,6BAAa,MAAM,IAAI,OAAM,CAAE;;;AC5WrE,IAAMA,UAAyB;;;AChB/B,IAAM,sBAAsB;AAC5B,IAAM,yBAAyB;AAC/B,IAAM,0BAA0B,sBAAsB,KAAK;AAoClE,IAAM,OAAO,IAAI,YAAW;AAC5B,IAAM,QAAQ,WAAW,KAAK,CAAC,IAAM,IAAM,IAAM,EAAI,CAAC;AACtD,IAAM,iBAAiB,KAAK,OAAO,qBAAqB;AASxD,SAAS,IAAI,OAAa;AACxB,QAAM,SAAS,IAAI,WAAW,CAAC;AAC/B,MAAI,SAAS,OAAO,MAAM,EAAE,UAAU,GAAG,KAAK;AAC9C,SAAO;AACT;AAEA,SAAS,IAAI,OAAa;AACxB,QAAM,SAAS,IAAI,WAAW,CAAC;AAC/B,MAAI,SAAS,OAAO,MAAM,EAAE,UAAU,GAAG,KAAK;AAC9C,SAAO;AACT;AAqMA,SAAS,OAAO,OAAmB;AACjC,QAAM,MAAM,IAAI,WAAW,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,YAAY,CAAC,CAAC;AACtE,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACxB,QAAI,IAAI,MAAM,MAAM;AACpB,cAAU,KAAK;EACjB;AACA,SAAO;AACT;AAEA,SAAS,KAAK,OAA0B;AACtC,QAAM,QAAQ,OAAO,UAAU,WAAW,KAAK,OAAO,KAAK,IAAI;AAC/D,MAAI,MAAM,aAAa;AAAQ,UAAM,IAAI,MAAM,+BAA+B;AAC9E,SAAO,OAAO,CAAC,IAAI,MAAM,UAAU,GAAG,KAAK,CAAC;AAC9C;AAGM,SAAU,yBAAyB,QAA4B;AACnE,QAAM,MAAM,OAAO;AACnB,SAAOC,QAAO,OAAO;IACnB,KAAK,OAAO,cAAc;IAC1B,KAAK,OAAO,KAAK;IACjB,IAAI,OAAO,SAAS;IACpB,IAAI,OAAO,kBAAkB;IAC7B,KAAK,MAAM,IAAI,YAAY,MAAM;IACjC,IAAI,KAAK,iBAAiB,CAAC;IAC3B,IAAI,KAAK,iBAAiB,CAAC;IAC3B,IAAI,KAAK,cAAc,CAAC;IACxB,IAAI,KAAK,qBAAqB,CAAC;GAChC,CAAC;AACJ;AAMM,SAAU,kBACd,WACA,WACA,QAA4B;AAE5B,QAAM,MAAM,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC/C,MAAI,UAAU,SAAS,SAAU,IAAI,SAAS;AAAQ,UAAM,IAAI,MAAM,6BAA6B;AACnG,SAAOA,QAAO,OAAO;IACnB,KAAK,OAAO,WAAW;IACvB,IAAI,UAAU,MAAM;IACpB,GAAG,UAAU,IAAI,IAAI;IACrB,IAAI,IAAI,MAAM;IACd,GAAG,IAAI,IAAI,GAAG;IACd,yBAAyB,MAAM;GAChC,CAAC;AACJ;;;AC/RA,IAAM,yBAAyB;AAGxB,IAAM,4BAA4B;AAClC,IAAM,4BAA4B;AAEzC,IAAM,qBACL;AAIM,SAAS,gBAAgB,OAA2B;AAC1D,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACzC,UAAM,KAAK,MAAM,CAAC;AAClB,UAAM,KAAK,MAAM,IAAI,CAAC;AACtB,UAAM,KAAK,MAAM,IAAI,CAAC;AACtB,cAAU,mBAAmB,MAAM,CAAC;AACpC,cAAU,oBAAqB,KAAK,MAAS,KAAM,OAAO,SAAY,IAAI,MAAM,EAAE;AAClF,QAAI,OAAO,OAAW;AACtB,cAAU,oBAAqB,KAAK,OAAS,KAAM,OAAO,SAAY,IAAI,MAAM,EAAE;AAClF,QAAI,OAAO,OAAW;AACtB,cAAU,mBAAmB,KAAK,EAAI;AAAA,EACvC;AACA,SAAO;AACR;AAEA,IAAM,0BAA0B,MAAM;AACrC,QAAM,QAAQ,IAAI,UAAU,GAAG,EAAE,KAAK,EAAE;AACxC,WAAS,IAAI,GAAG,IAAI,mBAAmB,QAAQ,KAAK;AACnD,UAAM,mBAAmB,WAAW,CAAC,CAAC,IAAI;AAAA,EAC3C;AACA,SAAO;AACR,GAAG;AAII,SAAS,gBAAgBC,OAA0B;AACzD,MAAIA,MAAK,SAAS,MAAM,GAAG;AAC1B,UAAM,IAAI,MAAM,mEAAmEA,KAAI,GAAG;AAAA,EAC3F;AACA,QAAM,QAAkB,CAAC;AACzB,MAAI,SAAS;AACb,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAIA,MAAK,QAAQ,KAAK;AACrC,UAAM,OAAOA,MAAK,WAAW,CAAC;AAC9B,UAAM,QAAQ,OAAO,MAAM,uBAAuB,IAAI,IAAI;AAC1D,QAAI,UAAU,UAAa,QAAQ,GAAG;AACrC,YAAM,IAAI,MAAM,4DAA4DA,KAAI,GAAG;AAAA,IACpF;AACA,aAAU,UAAU,IAAK;AACzB,YAAQ;AACR,QAAI,QAAQ,GAAG;AACd,cAAQ;AACR,YAAM,KAAM,UAAU,OAAQ,GAAI;AAAA,IACnC;AAAA,EACD;AACA,QAAM,UAAU,WAAW,KAAK,KAAK;AACrC,MAAI,gBAAgB,OAAO,MAAMA,OAAM;AACtC,UAAM,IAAI,MAAM,8DAA8DA,KAAI,GAAG;AAAA,EACtF;AACA,SAAO;AACR;AAIO,SAAS,uBAAuB,WAA+B;AACrE,QAAM,QAAQ,gBAAgB,SAAS;AACvC,MAAI,MAAM,eAAe,wBAAwB;AAChD,UAAM,IAAI;AAAA,MACT,sDAAsD,sBAAsB,mBAAmB,MAAM,UAAU;AAAA,IAChH;AAAA,EACD;AACA,SAAO;AACR;AAuCO,SAAS,sBAAsB,KAA0C;AAC/E,SAAO,uBAAuB,EAAE,WAAW,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,WAAW;AACxE;AA2CO,SAAS,4BACf,SACA,UAII,CAAC,GACqC;AAC1C,MAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,MAAM,QAAQ,OAAO,GAAG;AAC9E,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACrE;AACA,QAAM,SAAS,uBAAuB,SAAS,OAAO;AACtD,QAAM,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,kBAAkB,MAAS;AACtE,MAAI,MAAO,OAAM,IAAI,MAAM,4BAA4B,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE;AACrF,QAAM,YAAa,QAAoC,WAAW;AAClE,QAAM,OAAO,YAAY,MAAM;AAC/B,MAAI,SAAS,QAAW;AACvB,QAAI,QAAQ,aAAa;AACxB,YAAM,IAAI,MAAM,8FAA8F;AAAA,IAC/G;AACA,WAAO;AAAA,EACR;AACA,QAAM,UAAU,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,aAAa,CAAC;AAClE,QAAM,YAAa,UAAW,WAAW,KAAK,CAAC;AAC/C,QAAM,gBAAkB,QAAoC,QAAQ,KAAK,CAAC;AAC1E,QAAM,QAAqC,CAAC;AAC5C,YAAU,QAAQ,CAAC,UAAU,kBAAkB;AAC9C,QAAI,QAAQ,IAAI,aAAa,EAAG;AAChC,UAAM,WAAW,SAAS,MAAM;AAChC,UAAM,UAAW,SAAS,QAAQ,KAAK,CAAC,GAAiD,IAAI,CAAC,EAAE,MAAM,SAAS,OAAO;AAAA,MACrH;AAAA,MACA;AAAA,IACD,EAAE;AACF,QAAI;AACJ,QAAI,QAAQ,oBAAoB,QAAW;AAC1C,YAAM,SAAS,UAAU,eAAe,MAAM;AAE9C,UAAI,WAAW,OAAW;AAC1B,YAAM,kBAAkB,QAAQ,iBAAiB,OAAO,IAAI,CAAC,UAAU,MAAM,QAAQ,GAAG,MAAM;AAAA,IAC/F;AACA,UAAM,KAAK;AAAA,MACV;AAAA,MACA,QAAS,SAAS,eAAe,KAAK,SAAS;AAAA,MAC/C,cAAc,SAAS,cAAc;AAAA,MACrC,MAAM,SAAS,MAAM;AAAA,MACrB,WAAW,SAAS,WAAW;AAAA,MAC/B,cAAc,SAAS;AAAA,MACvB,UAAU,SAAS;AAAA,MACnB,gBAAgB,SAAS,kBAAkB;AAAA,MAC3C,iBAAiB,SAAS,mBAAmB;AAAA,MAC7C,GAAI,SAAS,kBAAkB,SAAY,CAAC,IAAI,EAAE,eAAe,SAAS,cAAc;AAAA,MACxF;AAAA,MACA,GAAI,QAAQ,SAAY,CAAC,IAAI,EAAE,IAAI;AAAA,IACpC,CAAC;AAAA,EACF,CAAC;AACD,SAAO;AAAA,IACN,MAAM,KAAK,KACT,IAAI,CAAC,SAAS,EAAE,KAAK,IAAI,KAAK,WAAW,uBAAuB,IAAI,SAAS,GAAG,YAAY,IAAI,WAAW,EAAE,EAC7G,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU;AAAA,IAC5C,UAAU,KAAK;AAAA,IACf;AAAA,EACD;AACD;AAGA,SAAS,gBAAgB,OAAwB;AAChD,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK,KAAK,QAAQ,KAAK,SAAS,aAAa,QAAQ;AAC/G;AASA,SAAS,UACR,eACA,QACmC;AACnC,QAAM,SAAS,IAAI,IAAI,cAAc,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC;AAC3E,QAAM,UAAU,CAAC,GAAG,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC;AACtG,MAAI,QAAQ,QAAQ,KAAK,CAAC,UAAU,UAAU,UAAa,MAAM,WAAW,MAAM,YAAY;AAC9F,MAAI,UAAU,QAAW;AACxB,UAAM,SAAS,QAAQ,KAAK,CAAC,UAAU,UAAU,MAAS;AAC1D,QAAI,WAAW,OAAW,QAAO;AACjC,UAAM,UAAU,OAAO,SAAS;AAChC,YAAQ,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,IAClD,OAAO,IAAI,QAAQ,CAAC,CAAC,IACrB,cAAc,KAAK,CAAC,UAAW,MAAM,KAAK,IAA4C,aAAa,MAAM,OAAO,MAAM,CAAC;AAC1H,QAAI,UAAU,OAAW,QAAO;AAAA,EACjC;AACA,QAAM,YAAY,gBAAgB,MAAM,WAAW,CAAC;AACpD,QAAM,KAAK,gBAAgB,MAAM,iBAAiB,CAAC;AACnD,QAAM,qBAAqB,gBAAgB,MAAM,oBAAoB,CAAC,KACrE,gBAAgB,aAAc,KAAK,YAAa,QAAS,IAAK,KAAK,YAAa,MAAO,CAAC;AACzF,QAAM,MAAM,MAAM,KAAK;AACvB,SAAO;AAAA,IACN,OAAO,OAAO,MAAM,OAAO,MAAM,WAAW,MAAM,OAAO,IAAI;AAAA,IAC7D;AAAA,IACA;AAAA,IACA,GAAI,QAAQ,UAAa,QAAQ,OAAO,CAAC,IAAI;AAAA,MAC5C,KAAK;AAAA,QACJ,WAAW,OAAO,IAAI,WAAW,MAAM,WAAW,IAAI,WAAW,IAAI;AAAA,QACrE,eAAe,gBAAgB,IAAI,eAAe,CAAC;AAAA,QACnD,eAAe,gBAAgB,IAAI,eAAe,CAAC;AAAA,QACnD,YAAY,gBAAgB,IAAI,YAAY,CAAC;AAAA,QAC7C,mBAAmB,gBAAgB,IAAI,mBAAmB,CAAC;AAAA,MAC5D;AAAA,IACD;AAAA,EACD;AACD;AAIO,SAAS,oBACf,UACA,KACA,OACyB;AAEzB,QAAM,UAAU,SAAS,KAAK,OAAO,CAAC,QAAQ,IAAI,cAAc,KAAK;AACrE,QAAM,SAAS,QAAQ,QAAQ,SAAS,CAAC;AACzC,SAAO,QAAQ,QAAQ,MAAM,OAAO,YAAY;AACjD;",
|
|
6
|
-
"names": ["sha256", "sha256", "text"]
|
|
3
|
+
"sources": ["../../mmt-base/src/mmtp.ts", "../../mmt-base/src/ntp-short.ts", "../../mmt-base/src/fec-block-profile.ts", "../../mmt-base/src/ip-address.ts", "../../mmt-base/src/multicast-catalog.ts", "../../mmt-base/src/fec-bindings.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/utils.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/_md.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/sha2.ts", "../../mmt-manifest-verify/node_modules/@noble/hashes/src/sha256.ts", "../../mmt-manifest-verify/src/bc-provenance-v2.ts", "../src/multicast-auth-catalog.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @mmt/container - MMTP Parser\n *\n * MMTP (MMT Protocol) packet parser based on ISO/IEC 23008-1:2023 Section 9.2\n *\n * MMTP header format (12 bytes):\n * - Byte 0: V(2) | C(1) | FEC_type(2) | r(1) | X(1) | R(1)\n * - Byte 1: reserved(2) | packet_type(6)\n * - Bytes 2-3: packet_id (big-endian)\n * - Bytes 4-7: timestamp (big-endian, 32-bit)\n * - Bytes 8-11: sequence_number (big-endian)\n *\n * FEC_type values:\n * - 0: No FEC / source packet\n * - 1: FEC mode 0 (source with FEC payload ID)\n * - 2: FEC repair packet mode 0\n * - 3: FEC repair packet mode 1\n *\n * @packageDocumentation\n */\n\nexport interface MMTPHeader {\n version: number\n /** Packet type: 0x00=MPU, 0x02=Signaling, 0x03=Repair */\n packetType: number\n packetId: number\n timestamp: bigint\n sequenceNumber: number\n payloadLength: number\n fecType: number\n /** C flag: header carries a 32-bit packet_counter. */\n packetCounterFlag: boolean\n packetCounter?: number\n /** X flag: header carries an extension header and value. */\n extensionFlag: boolean\n extension?: { type: number; data: Uint8Array }\n /** Byte offset at which the MMTP payload begins. */\n headerLength: number\n /** Random Access Point flag - indicates keyframe (ISO 23008-1, byte 0 bit 0) */\n rapFlag: boolean\n}\n\nexport interface MPUHeader {\n mpuSequenceNumber: number\n fragmentationIndicator: number // FI: 0=complete, 1=first, 2=middle, 3=last\n /** 8-bit MPU fragment_counter from byte 3. */\n fragmentCounter: number\n /** T flag: 1=timed media (14-byte MFU DU header), 0=non-timed (4-byte header) */\n timedFlag: number\n /** A flag: aggregated MPU payload. This strict profile requires A=0. */\n aggregationFlag: boolean\n /** MPU payload_length (bytes 0-1): bytes after the length field, including\n * the remaining six MPU-header bytes and all data-unit bytes. */\n payloadLength: number\n}\n\nexport interface MFUDuHeader {\n movieFragmentSequenceNumber: number\n sampleNumber: number\n offset: number\n priority: number\n dependencyCounter: number\n}\n\nexport interface MMTPPacket {\n header: MMTPHeader\n payload: Uint8Array\n}\n\nexport const MMTP_HEADER_SIZE = 12\nexport const MPU_HEADER_SIZE = 8\nexport const MFU_DU_HEADER_SIZE_TIMED = 14 // T=1: timed media (video/audio samples)\nexport const MFU_DU_HEADER_SIZE_NONTIMED = 4 // T=0: non-timed media (item_id only)\n\nconst MPU_LENGTH_FIELD_SIZE = 2\nconst MPU_HEADER_REMAINDER_SIZE = MPU_HEADER_SIZE - MPU_LENGTH_FIELD_SIZE\nexport const SOURCE_FEC_PAYLOAD_ID_SIZE = 4\n\n// MMTP Packet Types (ISO/IEC 23008-1:2023 Section 9.2.2)\nexport const PACKET_TYPE_MPU = 0x00\nexport const PACKET_TYPE_SIGNALING = 0x02\nexport const PACKET_TYPE_REPAIR = 0x03\n\n// AL-FEC Signaling Message ID (ISO/IEC 23008-1:2023 Amendment 1:2025)\nexport const ALFEC_MESSAGE_ID = 0x0203\n\n/**\n * MMTP `packet_id` of the signaling flow, which carries PACKET_TYPE_SIGNALING\n * packets such as the AL-FEC message (ALFEC_MESSAGE_ID). The FFmpeg `moq_mmt`\n * muxer sends that message on this packet_id into the same multicast group as\n * the media, and moqcast-draft (PR #44) makes advertising it for a track a\n * MUST NOT. A track's packet_id is therefore 1..MMTP_PACKET_ID_MAX.\n */\nexport const MMTP_SIGNALING_PACKET_ID = 0\n\n/** Largest value of the 16-bit MMTP `packet_id` header field. */\nexport const MMTP_PACKET_ID_MAX = 0xffff\n\n// fec_code_id_for_repair_flow code points (ISO/IEC 23008-10 Table 1;\n// draft-ramadan-moq-mmt-01 Section 8.3).\nexport const FEC_CODE_REED_SOLOMON = 1\nexport const FEC_CODE_RAPTORQ = 3\n\n/**\n * Parse MMTP header from buffer\n */\nexport function parseMMTPHeader(buffer: Uint8Array): MMTPHeader {\n if (buffer.length < MMTP_HEADER_SIZE) {\n throw new Error(`MMTP packet too small: ${buffer.length} bytes`)\n }\n\n const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength)\n\n const version = (buffer[0]! >> 6) & 0x03\n if (version !== 0) {\n throw new Error(`Unsupported MMTP version: ${version}`)\n }\n\n // Extract FEC type from byte 0, bits 3-4\n const fecType = (buffer[0]! >> 3) & 0x03\n const packetCounterFlag = (buffer[0]! & 0x20) !== 0\n const extensionFlag = (buffer[0]! & 0x02) !== 0\n\n // Extract RAP flag from byte 0, bit 0 (R bit - Random Access Point)\n const rapFlag = (buffer[0]! & 0x01) === 1\n\n // ISO/IEC 23008-1:2023 Figure 8 (V=0) places reserved(2) before\n // packet_type(6), so packet_type occupies the low six bits of byte 1.\n const packetType = buffer[1]! & 0x3F\n\n let offset = 2\n\n // Packet ID (2 bytes, big-endian)\n const packetId = view.getUint16(offset, false)\n offset += 2\n\n // Timestamp (4 bytes, big-endian)\n const timestamp = BigInt(view.getUint32(offset, false))\n offset += 4\n\n // Sequence number (4 bytes, big-endian)\n const sequenceNumber = view.getUint32(offset, false)\n offset += 4\n\n let packetCounter: number | undefined\n if (packetCounterFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in packet_counter')\n packetCounter = view.getUint32(offset, false)\n offset += 4\n }\n\n let extension: { type: number; data: Uint8Array } | undefined\n if (extensionFlag) {\n if (offset + 4 > buffer.length) throw new Error('MMTP packet truncated in header extension')\n const type = view.getUint16(offset, false)\n const length = view.getUint16(offset + 2, false)\n offset += 4\n if (offset + length > buffer.length) throw new Error('MMTP packet truncated in header extension value')\n extension = { type, data: buffer.subarray(offset, offset + length) }\n offset += length\n }\n\n return {\n version,\n packetType,\n packetId,\n timestamp,\n sequenceNumber,\n payloadLength: buffer.length - offset,\n fecType,\n packetCounterFlag,\n packetCounter,\n extensionFlag,\n extension,\n headerLength: offset,\n rapFlag,\n }\n}\n\n/**\n * The MMTP `packet_id` of `buffer`, or null exactly when parseMMTPHeader would\n * throw for it (short buffer, V != 0, truncated packet_counter or header\n * extension).\n *\n * This is the per-packet filter a receiver runs before any other work, so it\n * builds no header object, DataView or BigInt timestamp (BLO-36599). Use\n * parseMMTPHeader when any other header field is needed.\n */\nexport function readMMTPPacketId(buffer: Uint8Array): number | null {\n if (buffer.length < MMTP_HEADER_SIZE) return null\n const flags = buffer[0]!\n if (((flags >> 6) & 0x03) !== 0) return null\n let offset = MMTP_HEADER_SIZE\n if ((flags & 0x20) !== 0) {\n if (offset + 4 > buffer.length) return null\n offset += 4\n }\n if ((flags & 0x02) !== 0) {\n if (offset + 4 > buffer.length) return null\n const length = (buffer[offset + 2]! << 8) | buffer[offset + 3]!\n if (offset + 4 + length > buffer.length) return null\n }\n return (buffer[2]! << 8) | buffer[3]!\n}\n\n/**\n * Unwrap MMTP packet to extract payload\n */\nexport function unwrapMMTP(buffer: Uint8Array): MMTPPacket {\n const header = parseMMTPHeader(buffer)\n const payload = buffer.subarray(header.headerLength)\n\n return { header, payload }\n}\n\n/**\n * Parse MPU header from payload\n * ISO/IEC 23008-1:2023 Section 9.2.3.3\n *\n * MPU Header format (8 bytes) as written by FFmpeg/libmmt:\n * - Bytes 0-1: payload_length (16 bits, big-endian)\n * - Byte 2: FT(4) | T(1) | FI(2) | A(1)\n * - Byte 3: fragment_counter (8 bits)\n * - Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n */\nexport function parseMPUHeader(payload: Uint8Array): MPUHeader {\n if (payload.length < MPU_HEADER_SIZE) {\n throw new Error(`Payload too small for MPU header: ${payload.length} bytes`)\n }\n\n const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)\n\n // Bytes 0-1: payload_length (16 bits, big-endian)\n const payloadLength = view.getUint16(0, false)\n\n // Byte 2: FT(4) | T(1) | FI(2) | A(1)\n const flagsByte = payload[2]!\n const timedFlag = (flagsByte >> 3) & 0x01\n const fragmentationIndicator = (flagsByte >> 1) & 0x03\n const aggregationFlag = (flagsByte & 0x01) !== 0\n\n // Byte 3: fragment_counter (8 bits). The high bit is part of the counter in\n // the current libmmt/FFmpeg writer ABI.\n const fragmentCounter = payload[3]!\n\n // Bytes 4-7: MPU_sequence_number (32 bits, big-endian)\n const mpuSequenceNumber = view.getUint32(4, false)\n\n return {\n mpuSequenceNumber,\n fragmentationIndicator,\n fragmentCounter,\n timedFlag,\n aggregationFlag,\n payloadLength,\n }\n}\n\nexport function parseTimedMfuDuHeader(payload: Uint8Array, offset: number): MFUDuHeader | null {\n if (offset < 0 || offset + MFU_DU_HEADER_SIZE_TIMED > payload.length) return null\n const view = new DataView(payload.buffer, payload.byteOffset + offset, MFU_DU_HEADER_SIZE_TIMED)\n return {\n movieFragmentSequenceNumber: view.getUint32(0, false),\n sampleNumber: view.getUint32(4, false),\n offset: view.getUint32(8, false),\n priority: payload[offset + 12]!,\n dependencyCounter: payload[offset + 13]!,\n }\n}\n\n/**\n * Why `extractMFUPayload` refused a packet.\n *\n * These identifiers are part of the public contract: callers surface them in\n * diagnostics, so renaming one is a breaking change.\n */\nexport type MFURejectionGate =\n | 'packet-shorter-than-mmtp-plus-mpu-header'\n | 'mmtp-header-unparseable'\n | 'not-an-mpu-packet'\n | 'payload-shorter-than-mpu-header'\n | 'aggregation-flag-set'\n | 'mpu-payload-length-below-header-remainder'\n | 'mpu-declared-end-exceeds-payload'\n | 'mfu-offset-at-or-past-declared-end'\n | 'source-fec-payload-id-width-mismatch'\n | 'declared-trailers-unresolved'\n\nexport interface MFURejection {\n gate: MFURejectionGate\n /** Human-readable detail, including the byte arithmetic that decided it. */\n detail: string\n}\n\ninterface MfuPayloadLayout {\n hasMfuDuHeader: boolean\n mfuOffset: number\n dataEnd: number\n}\n\nfunction resolveMfuPayloadLayout(\n mpu: MPUHeader,\n fragmentType: number,\n payload: Uint8Array,\n): { layout: MfuPayloadLayout } | { rejection: MFURejection } {\n if (mpu.payloadLength < MPU_HEADER_REMAINDER_SIZE) {\n return {\n rejection: {\n gate: 'mpu-payload-length-below-header-remainder',\n detail: `payload_length=${mpu.payloadLength} < ${MPU_HEADER_REMAINDER_SIZE}`,\n },\n }\n }\n\n // ISO/IEC 23008-1 defines payload_length from immediately after its own\n // two-byte field. It therefore includes the remaining six MPU-header bytes.\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + mpu.payloadLength\n if (declaredEnd > payload.length) {\n return {\n rejection: {\n gate: 'mpu-declared-end-exceeds-payload',\n detail:\n `payload_length=${mpu.payloadLength} implies end=${declaredEnd}, ` +\n `payload is ${payload.length} bytes`,\n },\n }\n }\n\n const hasHeader = fragmentType === 2 && mpu.fragmentationIndicator <= 1\n const mfuHeaderSize = mpu.timedFlag ? MFU_DU_HEADER_SIZE_TIMED : MFU_DU_HEADER_SIZE_NONTIMED\n const mfuOffset = MPU_HEADER_SIZE + (hasHeader ? mfuHeaderSize : 0)\n if (mfuOffset >= declaredEnd) {\n return {\n rejection: {\n gate: 'mfu-offset-at-or-past-declared-end',\n detail: `mfuOffset=${mfuOffset} >= declaredEnd=${declaredEnd}`,\n },\n }\n }\n\n return {\n layout: {\n hasMfuDuHeader: hasHeader,\n mfuOffset,\n dataEnd: declaredEnd,\n },\n }\n}\n\n// Legacy detached per-packet authenticator suffix `[0xf0a1][len][auth]`. Its\n// producer was removed (BLO-16483) and nothing verifies it any more; provenance\n// is the signed per-group manifest. Receivers still skip an exact suffix so a\n// stale producer degrades to unauthenticated playback instead of dropped\n// packets. ponytail: hard-cut to reject once no deployed producer can emit it.\nconst LEGACY_TRAILER_EXT_TYPE = 0xf0a1\nconst LEGACY_TRAILER_HEADER_SIZE = 4\n\nfunction isEmptyOrExactLegacyTrailer(payload: Uint8Array, start: number, end: number): boolean {\n if (start === end) return true\n if (start < 0 || end > payload.length || start + LEGACY_TRAILER_HEADER_SIZE > end) {\n return false\n }\n\n const view = new DataView(\n payload.buffer,\n payload.byteOffset + start,\n end - start,\n )\n if (view.getUint16(0, false) !== LEGACY_TRAILER_EXT_TYPE) return false\n\n const authLength = view.getUint16(2, false)\n return start + LEGACY_TRAILER_HEADER_SIZE + authLength === end\n}\n\ninterface DeclaredTrailerLayout {\n hasFecId: boolean\n hasOverlayFecId: boolean\n}\n\n/** Options for extractMFUPayload and explainMFURejection. */\nexport interface MFUExtractOptions {\n /**\n * When true, do NOT subtract the Source FEC Payload ID from the payload\n * boundary, even if fecType >= 1. For FEC-recovered symbols whose FEC PID\n * was already stripped before WASM decoding.\n */\n skipFecId?: boolean\n /**\n * The catalog declares a keyframe overlay (`scope: \"keyframe\"` repair track)\n * for this packet's source. A RAP packet then carries two Source FEC Payload\n * IDs, base first and overlay last (BLO-32719 Clarification 7). This is a\n * catalog fact, never inferred: the header has no overlay bit, and a payload\n * four bytes shorter is byte-identical to a missing overlay. Mirrors\n * mmt-core `split_payload_and_source_fec_trailers(overlay_declared)`.\n */\n overlayDeclared?: boolean\n}\n\nfunction resolveDeclaredTrailers(\n payload: Uint8Array,\n declaredEnd: number,\n fecType: number,\n options?: MFUExtractOptions,\n rapFlag = false,\n): DeclaredTrailerLayout | null {\n if (declaredEnd > payload.length) return null\n\n if (options?.skipFecId) {\n return isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)\n ? { hasFecId: false, hasOverlayFecId: false }\n : null\n }\n\n const overlay = options?.overlayDeclared === true && rapFlag\n if (fecType === 1) {\n const fecStart = payload.length - sourceFecTrailerSize(overlay)\n if (fecStart < declaredEnd || !isEmptyOrExactLegacyTrailer(payload, declaredEnd, fecStart)) {\n return null\n }\n return { hasFecId: true, hasOverlayFecId: overlay }\n }\n // An overlay ID rides behind a base ID the header says is absent.\n if (fecType !== 0 || overlay) return null\n\n if (isEmptyOrExactLegacyTrailer(payload, declaredEnd, payload.length)) {\n return { hasFecId: false, hasOverlayFecId: false }\n }\n\n return null\n}\n\nfunction sourceFecTrailerSize(overlay: boolean): number {\n return overlay ? 2 * SOURCE_FEC_PAYLOAD_ID_SIZE : SOURCE_FEC_PAYLOAD_ID_SIZE\n}\n\n/**\n * Extract NAL unit payload from MMTP packet\n */\nexport function extractNALUnit(mmtpPacket: Uint8Array): Uint8Array {\n const extraction = extractMFUPayload(mmtpPacket)\n if (!extraction) throw new Error('Malformed MMTP MPU payload')\n return extraction.data\n}\n\n/** Result of extracting MFU payload from an MMTP packet */\nexport interface MFUExtraction {\n /** MMTP header fields */\n header: MMTPHeader\n /** MPU header fields */\n mpu: MPUHeader\n /** Fragment type (FT field, 4 bits): 0=metadata, 2=MFU data */\n fragmentType: number\n /** MFU payload data (video/audio bytes, headers stripped, FEC ID excluded) */\n data: Uint8Array\n /** Parsed timed MFU DU header when present, otherwise null */\n mfuDuHeader: MFUDuHeader | null\n /** Non-timed MFU Item_ID when the 4-byte DU header is present. */\n itemId: number | null\n /** Source FEC Payload ID if present (`FEC_type == 1`), or null */\n fecPayloadId: { ssId: number } | null\n /** Keyframe-overlay Source FEC Payload ID: RAP packets with `overlayDeclared` only. */\n overlayFecPayloadId: { ssId: number } | null\n}\n\n/**\n * Extract MFU payload from a raw MMTP packet.\n *\n * Handles all the byte arithmetic for:\n * - MMTP header (12 bytes)\n * - MPU sub-header (8 bytes, payloadLength field)\n * - MFU DU header (14 bytes timed / 4 bytes non-timed, only for FI \u2264 1)\n * - Source FEC Payload ID (4 bytes at end, only when `FEC_type == 1`)\n *\n * `payloadLength` is the ISO length-after-field value: six remaining MPU-header\n * bytes, then the MFU DU header when present, then media. The legacy\n * authenticator suffix and the Source FEC trailer are outside that declared span and must consume the exact suffix.\n *\n * @param mmtpPacket Raw MMTP packet (from multicast or MoQ)\n * @param options.skipFecId When true, do NOT subtract the 4-byte Source FEC Payload ID\n * from the payload boundary, even if fecType >= 1 in the MMTP header. Use this for\n * FEC-recovered symbols where the FEC PID was already stripped before WASM decoding\n * but the fecType bits in byte 0 still reflect the original packet (FEC Type=1 per\n * draft-ramadan-moq-mmt-01 \u00A73.1).\n * @param options.overlayDeclared The catalog declares a keyframe overlay for this\n * source, so a RAP packet carries an 8-byte trailer (see MFUExtractOptions).\n * @returns Extracted MFU data + metadata, or null if packet is too small\n */\ninterface MfuPlan {\n header: MMTPHeader\n payload: Uint8Array\n mpu: MPUHeader\n fragmentType: number\n layout: MfuPayloadLayout\n trailerLayout: DeclaredTrailerLayout\n}\n\n/**\n * Single source of truth for whether a packet yields an MFU payload.\n *\n * `extractMFUPayload` and `explainMFURejection` both delegate here, so the\n * reason reported to operators can never drift from the decision the parser\n * actually made.\n */\nfunction planMfuExtraction(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n parsedHeader?: MMTPHeader,\n): { plan: MfuPlan } | { rejection: MFURejection } {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'packet-shorter-than-mmtp-plus-mpu-header',\n detail: `${mmtpPacket.length} bytes < ${MMTP_HEADER_SIZE + MPU_HEADER_SIZE}`,\n },\n }\n }\n\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch (error) {\n return {\n rejection: {\n gate: 'mmtp-header-unparseable',\n detail: error instanceof Error ? error.message : String(error),\n },\n }\n }\n }\n if (header.packetType !== PACKET_TYPE_MPU) {\n return {\n rejection: {\n gate: 'not-an-mpu-packet',\n detail: `packetType=0x${header.packetType.toString(16).padStart(2, '0')}`,\n },\n }\n }\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) {\n return {\n rejection: {\n gate: 'payload-shorter-than-mpu-header',\n detail: `${payload.length} bytes < ${MPU_HEADER_SIZE}`,\n },\n }\n }\n\n const mpu = parseMPUHeader(payload)\n if (mpu.aggregationFlag) {\n return {\n rejection: { gate: 'aggregation-flag-set', detail: 'strict profile requires A=0' },\n }\n }\n\n // Fragment type from byte 2 upper nibble (not returned by parseMPUHeader)\n const fragmentType = (payload[2]! >> 4) & 0x0f\n\n const layoutResult = resolveMfuPayloadLayout(mpu, fragmentType, payload)\n if ('rejection' in layoutResult) return layoutResult\n const { layout } = layoutResult\n\n const trailerLayout = resolveDeclaredTrailers(\n payload,\n layout.dataEnd,\n header.fecType,\n options,\n header.rapFlag,\n )\n if (!trailerLayout) {\n // The most common cause is a producer whose Source FEC Payload ID width\n // disagrees with ISO/IEC 23008-1 \u00A7C.5.2. Name that explicitly rather than\n // leaving operators to infer it from a generic trailer error.\n if (!options?.skipFecId && header.fecType === 1) {\n const observed = payload.length - layout.dataEnd\n const expected = sourceFecTrailerSize(options?.overlayDeclared === true && header.rapFlag)\n if (observed !== expected) {\n return {\n rejection: {\n gate: 'source-fec-payload-id-width-mismatch',\n detail:\n `producer wrote a ${observed}-byte Source FEC Payload ID, ` +\n `ISO/IEC 23008-1 \u00A7C.5.2 requires ${expected} ` +\n `(payload=${payload.length}, declaredEnd=${layout.dataEnd})`,\n },\n }\n }\n }\n return {\n rejection: {\n gate: 'declared-trailers-unresolved',\n detail:\n `trailer bytes [${layout.dataEnd}, ${payload.length}) are neither empty ` +\n `nor an exact legacy authenticator trailer (fecType=${header.fecType})`,\n },\n }\n }\n\n return { plan: { header, payload, mpu, fragmentType, layout, trailerLayout } }\n}\n\n/**\n * Report why `extractMFUPayload` would refuse this packet, or null if it would\n * accept it.\n *\n * `extractMFUPayload` returns bare `null` on ten distinct conditions, which\n * makes a producer/consumer framing disagreement indistinguishable from a\n * publisher that never started \u2014 both render as silence. Use this wherever\n * that distinction matters: diagnostics, contract tests, and operator-facing\n * logs.\n */\nexport function explainMFURejection(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n): MFURejection | null {\n const result = planMfuExtraction(mmtpPacket, options)\n return 'rejection' in result ? result.rejection : null\n}\n\nexport function extractMFUPayload(\n mmtpPacket: Uint8Array,\n options?: MFUExtractOptions,\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options))\n}\n\n/**\n * extractMFUPayload for a caller that already holds this packet's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet; the\n * result is then identical to `extractMFUPayload(mmtpPacket, options)`.\n */\nexport function extractMFUPayloadWithHeader(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n options?: MFUExtractOptions,\n): MFUExtraction | null {\n return extractPlannedMfu(mmtpPacket, planMfuExtraction(mmtpPacket, options, header))\n}\n\nfunction extractPlannedMfu(\n mmtpPacket: Uint8Array,\n result: { plan: MfuPlan } | { rejection: MFURejection },\n): MFUExtraction | null {\n if ('rejection' in result) return null\n const { header, payload, mpu, fragmentType, layout, trailerLayout } = result.plan\n\n const mfuDuHeaderPresent = layout.hasMfuDuHeader\n const mfuOffset = layout.mfuOffset\n const dataEnd = layout.dataEnd\n\n const mfuDuHeader =\n mfuDuHeaderPresent && mpu.timedFlag === 1\n ? parseTimedMfuDuHeader(payload, MPU_HEADER_SIZE)\n : null\n const itemId =\n mfuDuHeaderPresent && mpu.timedFlag === 0\n ? new DataView(payload.buffer, payload.byteOffset + MPU_HEADER_SIZE, 4).getUint32(0, false)\n : null\n\n const data = payload.subarray(mfuOffset, dataEnd)\n\n const trailerStart =\n mmtpPacket.length - sourceFecTrailerSize(trailerLayout.hasOverlayFecId)\n const ssIdAt = (offset: number) => ({\n ssId: new DataView(mmtpPacket.buffer, mmtpPacket.byteOffset + offset, 4).getUint32(0, false),\n })\n const fecPayloadId = trailerLayout.hasFecId ? ssIdAt(trailerStart) : null\n const overlayFecPayloadId = trailerLayout.hasOverlayFecId\n ? ssIdAt(trailerStart + SOURCE_FEC_PAYLOAD_ID_SIZE)\n : null\n\n return { header, mpu, fragmentType, data, mfuDuHeader, itemId, fecPayloadId, overlayFecPayloadId }\n}\n\n/**\n * Extract timestamp from MMTP packet\n */\nexport function extractTimestamp(mmtpPacket: Uint8Array): bigint {\n const { header } = unwrapMMTP(mmtpPacket)\n return header.timestamp\n}\n\n/**\n * Check if MMTP packet is an FEC repair packet\n * FEC_type 2 and 3 are repair packets\n */\nexport function isFecRepairPacket(mmtpPacket: Uint8Array): boolean {\n if (mmtpPacket.length < 1) return false\n const fecType = (mmtpPacket[0]! >> 3) & 0x03\n return fecType >= 2\n}\n\n/**\n * Extract Source FEC Payload ID (SS_ID) from the last 4 bytes of an MMTP\n * source packet per ISO/IEC 23008-1:2023 Section C.5.2 Figure C.10.\n *\n * SS_ID is a flat 32-bit monotonic counter (incremented by 1 per packet\n * for ssbg_mode0 and ssbg_mode1). Block membership and ESI use the\n * configured K; repair SS_Start identifies the first SS_ID of the block and\n * SSB_length records the authored source-symbol count:\n * SBN = floor(SS_ID / K), ESI = SS_ID % K\n * SS_Start = SBN * K (first SS_ID of the block)\n *\n * This matches FFmpeg moqenc_mmt.c, the drafts (draft-ramadan-moq-fec \u00A76.2),\n * and ATSC A/331 \u00A78.1.2.5 which defers to ISO 23008-1 Annex C.\n */\nexport function extractFecPayloadId(mmtpPacket: Uint8Array): { ssId: number } | null {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header ? sourceFecPayloadIdOf(mmtpPacket, header) : null\n}\n\n/**\n * Return true when FEC_type=1 and the exact suffix after the ISO-declared MPU\n * payload contains a four-byte Source FEC Payload ID.\n */\nexport function hasSourceFecPayloadId(mmtpPacket: Uint8Array): boolean {\n const header = parseHeaderForSourceFecId(mmtpPacket)\n return header !== null && sourceFecPayloadIdOf(mmtpPacket, header) !== null\n}\n\nfunction parseHeaderForSourceFecId(mmtpPacket: Uint8Array): MMTPHeader | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n try {\n return parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n}\n\n/**\n * The Source FEC Payload ID of a packet whose MMTP header the caller already\n * parsed, or null when the packet carries none: present only when FEC_type=1\n * and the exact suffix after the ISO-declared MPU payload holds the four-byte\n * ID (ISO/IEC 23008-1 \u00A7C.5.2). Same answer as extractFecPayloadId, without a\n * second header parse (BLO-36599).\n *\n * `header` must be `parseMMTPHeader(mmtpPacket)` of this same packet.\n */\nexport function sourceFecPayloadIdOf(\n mmtpPacket: Uint8Array,\n header: MMTPHeader,\n): { ssId: number } | null {\n if (mmtpPacket.length < MMTP_HEADER_SIZE + MPU_HEADER_SIZE) return null\n if (header.packetType !== PACKET_TYPE_MPU) return null\n\n const payload = mmtpPacket.subarray(header.headerLength)\n if (payload.length < MPU_HEADER_SIZE) return null\n const payloadLength = (payload[0]! << 8) | payload[1]!\n if (payloadLength < MPU_HEADER_REMAINDER_SIZE) return null\n const declaredEnd = MPU_LENGTH_FIELD_SIZE + payloadLength\n if (!resolveDeclaredTrailers(payload, declaredEnd, header.fecType)?.hasFecId) return null\n\n // Flat 4-byte SS_ID: encoder writes block_id * K + symbol_id\n const off = mmtpPacket.length - SOURCE_FEC_PAYLOAD_ID_SIZE\n return {\n ssId:\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0,\n }\n}\n\n/** Size of Repair FEC Payload ID per ISO/IEC 23008-1 Section C.5.3 */\nexport const REPAIR_FEC_PID_SIZE = 13\n\n/**\n * Parse the 13-byte Repair FEC Payload ID after the complete dynamic MMTP header.\n * `parsedHeader`, when given, must be `parseMMTPHeader(mmtpPacket)` of this\n * same packet; it spares the second header parse (BLO-36599).\n */\nfunction readRepairFecPayloadIdFields(mmtpPacket: Uint8Array, parsedHeader?: MMTPHeader): {\n packetId: number; ssStart: number; rsbLength: number; rsId: number; ssbLength: number; dataOffset: number\n} | null {\n let header: MMTPHeader\n if (parsedHeader) {\n header = parsedHeader\n } else {\n try {\n header = parseMMTPHeader(mmtpPacket)\n } catch {\n return null\n }\n }\n if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2) return null\n\n const off = header.headerLength\n if (mmtpPacket.length < off + REPAIR_FEC_PID_SIZE) return null\n const ssStart =\n ((mmtpPacket[off]! << 24) |\n (mmtpPacket[off + 1]! << 16) |\n (mmtpPacket[off + 2]! << 8) |\n mmtpPacket[off + 3]!) >>>\n 0 // 32 bits\n const rsbLength = (mmtpPacket[off + 4]! << 16) | (mmtpPacket[off + 5]! << 8) | mmtpPacket[off + 6]! // 24 bits\n const rsId = (mmtpPacket[off + 7]! << 16) | (mmtpPacket[off + 8]! << 8) | mmtpPacket[off + 9]! // 24 bits\n const ssbLength = (mmtpPacket[off + 10]! << 16) | (mmtpPacket[off + 11]! << 8) | mmtpPacket[off + 12]! // 24 bits\n return {\n packetId: header.packetId,\n ssStart,\n rsbLength,\n rsId,\n ssbLength,\n dataOffset: off + REPAIR_FEC_PID_SIZE,\n }\n}\n\n/**\n * Extract Repair FEC Payload ID from a raw-multicast MMTP repair packet.\n * Per ISO/IEC 23008-1:2023 Section C.5.3 (ssbg_mode0, one-stage, no FFSRP_TS):\n * [MMTP header][SS_Start:4][RSB_length:3][RS_ID:3][SSB_length:3][repair data]\n * OTI is delivered via AL-FEC signaling message (Table C.3), not per-packet.\n *\n * Byte 1 is reserved(2)|packet_type(6) for V=0, so repair type 0x03 is 0x03.\n * Pre-migration high-bits bytes (0x0c) are rejected \u2014 no compatibility decode.\n */\nexport function extractRepairFecPayloadId(mmtpPacket: Uint8Array): {\n ssStart: number; rsbLength: number; rsId: number; ssbLength: number\n} | null {\n const fields = readRepairFecPayloadIdFields(mmtpPacket)\n if (!fields) return null\n return {\n ssStart: fields.ssStart,\n rsbLength: fields.rsbLength,\n rsId: fields.rsId,\n ssbLength: fields.ssbLength,\n }\n}\n\n/** Parsed MoQ repair frame per ISO/IEC 23008-1 Section C.4.3 + C.5.3 */\nexport interface MoqRepairFrame {\n /** MMTP packetId (1=video, 2=audio) \u2014 for routing to correct FEC decoder */\n packetId: number\n /** SS_Start (32 bits): SS_ID of first source symbol in block */\n ssStart: number\n /** RSB_length (24 bits): number of repair symbols (P) */\n rsbLength: number\n /** RS_ID (24 bits): repair symbol index (0-based) */\n rsId: number\n /** SSB_length (24 bits): authored source-symbol count (1..=configured K) */\n ssbLength: number\n /** Repair symbol data after the dynamic MMTP header and 13-byte FEC payload ID. */\n symbolData: Uint8Array\n}\n\n/**\n * Parse a MoQ repair frame (ISO/IEC 23008-1 Annex C format).\n *\n * Wire format per ISO 23008-1 \u00A7C.4.3 + \u00A7C.5.3:\n * MMTP Header: fixed 12 bytes plus C/X optional fields\n * then SS_Start (4 bytes) \u2192 block identifier\n * then RSB_length (3 bytes) \u2192 P\n * then RS_ID (3 bytes) \u2192 ESI = K + RS_ID\n * then SSB_length (3 bytes) \u2192 authored source-symbol count (1..=K)\n * then Repair Symbol Data (T bytes)\n *\n * @param frame - Raw MoQ repair frame bytes\n * @returns Parsed frame or null if invalid\n */\nexport function parseMoqRepairFrame(frame: Uint8Array): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame))\n}\n\n/**\n * parseMoqRepairFrame for a caller that already holds this frame's parsed MMTP\n * header, so the header is not parsed a second time (BLO-36599). The router's\n * multicast repair branch uses it: one repair packet in every K + P otherwise\n * paid two header parses.\n *\n * `header` must be `parseMMTPHeader(frame)` of this same frame; the result is\n * then identical to `parseMoqRepairFrame(frame)`.\n */\nexport function parseMoqRepairFrameWithHeader(\n frame: Uint8Array,\n header: MMTPHeader,\n): MoqRepairFrame | null {\n return moqRepairFrameFrom(frame, readRepairFecPayloadIdFields(frame, header))\n}\n\nfunction moqRepairFrameFrom(\n frame: Uint8Array,\n repairId: ReturnType<typeof readRepairFecPayloadIdFields>,\n checkRsId = true,\n): MoqRepairFrame | null {\n if (!repairId) return null\n\n const symbolData = frame.subarray(repairId.dataOffset)\n if (\n repairId.ssbLength === 0 ||\n (checkRsId && repairId.rsId >= repairId.rsbLength) ||\n symbolData.length === 0\n ) return null\n\n return {\n packetId: repairId.packetId,\n ssStart: repairId.ssStart,\n rsbLength: repairId.rsbLength,\n rsId: repairId.rsId,\n ssbLength: repairId.ssbLength,\n symbolData,\n }\n}\n\n/**\n * Why a repair object of an mmtp track is discarded on admission:\n * `repairLength` -- the object is not exactly 12 + 13 + T bytes with a\n * 12-byte MMTP header (C = 0, X = 0; draft-ramadan-moq-mmt-02 7.3, judged\n * before any Repair FEC Payload ID field is read); `repairRsId` -- RS_ID is not\n * below RSB_length, under every scheme; `repairSchemeBound` -- the object\n * breaks its scheme's bound: reed-solomon SSB_length + RSB_length > 255, which\n * also caps RS_ID at 253, or raptorq SSB_length + RSB_length - 1 > 2^24 - 1\n * (draft-ramadan-moq-fec-core-00 7.5 and 10.3, -block-codes-00 7).\n */\nexport type RepairObjectReject = 'repairLength' | 'repairRsId' | 'repairSchemeBound'\n\n/**\n * Admission of one mmtp repair object under its instance's T and scheme. The\n * caller names the instance (by the repair track it subscribed); nothing here\n * infers it from the fields. `repairRsId` and `repairSchemeBound` carry the\n * frame so the caller can first route an RSB_length below a held layer's\n * O_i + P_i to the 7.3.2 stop, which takes precedence over the discard.\n * `null`: not an iso repair packet, or SSB_length 0.\n */\nexport function admitMmtpRepairObject(\n packet: Uint8Array,\n header: MMTPHeader,\n symbolSize: number,\n algorithm: string | undefined,\n): { frame: MoqRepairFrame; reject?: Exclude<RepairObjectReject, 'repairLength'> } | { reject: 'repairLength' } | null {\n if (header.packetType !== PACKET_TYPE_REPAIR || header.fecType !== 2) return null\n if (header.headerLength !== MMTP_HEADER_SIZE || packet.length !== MMTP_HEADER_SIZE + REPAIR_FEC_PID_SIZE + symbolSize) {\n return { reject: 'repairLength' }\n }\n const frame = moqRepairFrameFrom(packet, readRepairFecPayloadIdFields(packet, header), false)\n if (!frame) return null\n if (frame.rsId >= frame.rsbLength) return { frame, reject: 'repairRsId' }\n const blockSymbols = frame.ssbLength + frame.rsbLength\n if (algorithm === 'reed-solomon' ? blockSymbols > 255 : algorithm === 'raptorq' && blockSymbols - 1 > 0xffffff) {\n return { frame, reject: 'repairSchemeBound' }\n }\n return { frame }\n}\n\n// The signalling-message parsers (9.3.4.2 payload header, AL-FEC message,\n// PA/MPT) live in ./signalling.ts.\n", "/**\n * NTP short format (RFC 5905 \u00A76): an unsigned 32-bit, 16.16 fixed-point count\n * of seconds. The MMTP header `timestamp` field (ISO/IEC 23008-1 \u00A79.2.2)\n * carries each packet's UTC sending instant in this format, so the value wraps\n * to 0 every 2^16 s (~18.2 h).\n *\n * Plain numeric order on these values breaks at every wrap: a retained\n * `Math.max` stays at the pre-wrap value and every later instant compares as\n * older. Order send times only with {@link compareNtpShort}, which applies\n * RFC 1982 serial-number arithmetic over the field's width.\n */\n\n/** Width in bits of the MMTP `timestamp` field (NTP short). */\nexport const NTP_SHORT_BITS = 32\n\n/** Number of distinct NTP-short values; the field wraps to 0 here. */\nexport const NTP_SHORT_MODULUS = 2 ** NTP_SHORT_BITS\n\n/**\n * RFC 1982 \u00A73.2's `2^(SERIAL_BITS - 1)`. Two values whose forward distance is\n * below this are ordered; exactly this far apart they are not ordered at all.\n * Two instants must be less than this far apart (2^15 s, ~9.1 h of send time)\n * for the comparison to give their true order.\n */\nexport const NTP_SHORT_HALF_RANGE = 2 ** (NTP_SHORT_BITS - 1)\n\n/**\n * Serial order of one NTP-short value relative to another.\n *\n * `'unordered'` is RFC 1982's undefined case: the values are exactly\n * {@link NTP_SHORT_HALF_RANGE} apart, so neither precedes the other. It is\n * never folded into `'before'` or `'after'`; each caller decides what an\n * unorderable pair means for it.\n */\nexport type NtpShortOrder = 'before' | 'equal' | 'after' | 'unordered'\n\n/** True when `value` is an NTP-short value: an integer in [0, NTP_SHORT_MODULUS). */\nexport function isNtpShort(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value < NTP_SHORT_MODULUS\n}\n\n/**\n * RFC 1982 serial comparison of NTP-short `a` against `b`: `'after'` when `a`\n * is the later instant, `'before'` when it is the earlier one.\n *\n * The answer is the true order only while the two instants are less than\n * {@link NTP_SHORT_HALF_RANGE} apart. Past that a 32-bit field cannot tell\n * later from earlier, and the result names whichever direction is nearer.\n * This is not a total order (it is not transitive across the whole space), so\n * it must never be used to sort.\n *\n * @throws {RangeError} if either operand is not an NTP-short value\n */\nexport function compareNtpShort(a: number, b: number): NtpShortOrder {\n if (!isNtpShort(a) || !isNtpShort(b)) {\n throw new RangeError(\n `NTP-short values must be integers in [0, 2^${NTP_SHORT_BITS}), got ${String(a)} and ${String(b)}`,\n )\n }\n if (a === b) return 'equal'\n // Forward distance from b to a, reduced into [1, NTP_SHORT_MODULUS). Both\n // operands are below 2^32, so every step is exact in a double.\n const forward = (a - b + NTP_SHORT_MODULUS) % NTP_SHORT_MODULUS\n if (forward < NTP_SHORT_HALF_RANGE) return 'after'\n if (forward > NTP_SHORT_HALF_RANGE) return 'before'\n return 'unordered'\n}\n", "/**\n * FEC block profile (BLO-35597, ISO/IEC 23008-1 Annex C).\n *\n * Every catalog `fec` object that declares an FEC instance names its block\n * profile in `fec.blockProfile`. A receiver applies all of a profile's rules\n * or none of them, so the token is the one explicit per-stream discriminator:\n * it is never defaulted and never inferred from the wire.\n *\n * draft-ramadan-moq-fec \u00A75.1 defines exactly one value, and it is the only\n * profile this library implements:\n *\n * - `iso-ssbg1-v1`: ssbg_mode1 (C.3.3): a source symbol is\n * `u16 length || protected packet || zero padding` to T; a block is a\n * K' = SSB_length RaptorQ block (C.5.3) and repair ESI = SSB_length + RS_ID;\n * SS_ID increments by one per FEC source packet and wraps at 2^32 (C.5.2),\n * so a block is located by membership in `[SS_Start, SS_Start + SSB_length)`\n * under RFC 1982 serial arithmetic; the MoQ repair group id is the 62-bit\n * wrap-epoch-extended SS_Start. The AL-FEC coding byte for this profile is\n * 0x14 (one-stage, ssbg_mode1, fec_payload_id_mode 0, Amd 1:2025 Table C.3).\n *\n * A track whose `blockProfile` is absent or names any other token (the\n * retired Blockcast-private `k-padded-v1` included) has no FEC: a receiver\n * consumes its source without FEC and joins none of its repair tracks\n * (draft-ramadan-moq-fec \u00A75.1). `parseFecBlockProfile` throws for such a\n * token; the per-track catalog resolver turns the same rule into a typed\n * per-track \"FEC unavailable\" outcome instead of failing the session.\n */\nimport { serial32Add, serial32Distance, serial32Precedes } from './serial-number.js'\n\n/** The block profile a catalog `fec.blockProfile` may name: the one this library implements. */\nexport type FecBlockProfile = 'iso-ssbg1-v1'\n\n/** Every accepted `fec.blockProfile` token. */\nexport const FEC_BLOCK_PROFILES: readonly FecBlockProfile[] = Object.freeze([\n 'iso-ssbg1-v1',\n])\n\n/** True for a block-profile token this library implements. */\nexport function isFecBlockProfile(value: unknown): value is FecBlockProfile {\n return value === 'iso-ssbg1-v1'\n}\n\n/**\n * Read a required `fec.blockProfile`. Throws on an absent token and on any\n * token other than `iso-ssbg1-v1`: there is no default and nothing is\n * inferred from the wire (draft-ramadan-moq-fec \u00A75.1).\n *\n * @param where the catalog path of the field, for the error message.\n */\nexport function parseFecBlockProfile(value: unknown, where: string): FecBlockProfile {\n if (isFecBlockProfile(value)) return value\n if (value === undefined) {\n throw new Error(`${where} is required: name the FEC block profile (\"iso-ssbg1-v1\")`)\n }\n throw new Error(`${where} must be \"iso-ssbg1-v1\" (got ${JSON.stringify(value)})`)\n}\n\n/** ISO/IEC 23008-1 C.3.3 ssbg_mode1: the two-octet packet length field. */\nexport const SSBG1_LENGTH_FIELD_BYTES = 2\n\n/** The largest protected packet an ssbg_mode1 symbol of size T can carry. */\nexport function ssbg1MaxPacketLength(symbolSize: number): number {\n return symbolSize - SSBG1_LENGTH_FIELD_BYTES\n}\n\n/** Whether a protected packet of `packetLength` bytes fits one T-byte ssbg_mode1 symbol. */\nexport function ssbg1FitsSymbol(packetLength: number, symbolSize: number): boolean {\n return (\n Number.isSafeInteger(packetLength) &&\n packetLength >= 0 &&\n packetLength <= ssbg1MaxPacketLength(symbolSize) &&\n packetLength <= 0xffff\n )\n}\n\n/**\n * Build one ssbg_mode1 source symbol (C.3.3): the protected packet's length\n * in two octets, network byte order, then the packet, then zero octets up to\n * T. The protected packet is the MMTP packet before its Source FEC Payload ID\n * is appended (C.4.2).\n *\n * @throws when `length + 2 > T`: the packet cannot be protected under this\n * profile, and silently truncating it would corrupt every repair symbol.\n */\nexport function buildSsbg1SourceSymbol(packet: Uint8Array, symbolSize: number): Uint8Array {\n if (!Number.isSafeInteger(symbolSize) || symbolSize <= SSBG1_LENGTH_FIELD_BYTES) {\n throw new Error(`ssbg_mode1 symbol size must exceed ${SSBG1_LENGTH_FIELD_BYTES} (got ${symbolSize})`)\n }\n if (!ssbg1FitsSymbol(packet.byteLength, symbolSize)) {\n throw new Error(\n `ssbg_mode1 source symbol needs ${packet.byteLength} + ${SSBG1_LENGTH_FIELD_BYTES} bytes but T is ${symbolSize}`,\n )\n }\n const symbol = new Uint8Array(symbolSize)\n symbol[0] = (packet.byteLength >>> 8) & 0xff\n symbol[1] = packet.byteLength & 0xff\n symbol.set(packet, SSBG1_LENGTH_FIELD_BYTES)\n return symbol\n}\n\n/**\n * Recover the protected packet from one ssbg_mode1 source symbol: the bytes\n * the length field names. Returns null, never a guess, when the length field\n * points past the symbol or any padding octet is non-zero (C.3.3 padding is\n * all 00h), since either means the recovered symbol is not what was sent.\n */\nexport function trimSsbg1SourceSymbol(symbol: Uint8Array): Uint8Array | null {\n if (symbol.byteLength < SSBG1_LENGTH_FIELD_BYTES) return null\n const length = (symbol[0]! << 8) | symbol[1]!\n const end = SSBG1_LENGTH_FIELD_BYTES + length\n if (end > symbol.byteLength) return null\n for (let i = end; i < symbol.byteLength; i++) {\n if (symbol[i] !== 0) return null\n }\n return symbol.subarray(SSBG1_LENGTH_FIELD_BYTES, end)\n}\n\n/** The SS_ID after the last source symbol of `[ssStart, ssStart + ssbLength)`. */\nexport function isoBlockEnd(ssStart: number, ssbLength: number): number {\n return serial32Add(ssStart, ssbLength)\n}\n\n/**\n * Range membership (C.5.2, C.5.3): whether `ssId` is one of the\n * `ssbLength` consecutive SS_IDs starting at `ssStart`, modulo 2^32.\n */\nexport function isoBlockContains(ssStart: number, ssbLength: number, ssId: number): boolean {\n return serial32Distance(ssStart, ssId) < ssbLength\n}\n\n/**\n * A lower bound on how many blocks the block starting at `a` lies behind the\n * one starting at `b` (0 when `a` does not precede `b`). An iso-ssbg1-v1\n * block holds at most `kMax` source symbols (the catalog `sourceSymbols`, the\n * AL-FEC maximum_k_for_repair_flow), so `n * kMax` SS_IDs always span at least\n * `n` blocks. Every interleave horizon in the receiver (decoder retention,\n * worker retirement, ordered-relay settlement) uses this one bound, so the\n * manager's horizon can never fall inside the decoder's.\n */\nexport function isoBlocksBehindLowerBound(a: number, b: number, kMax: number): number {\n if (!Number.isSafeInteger(kMax) || kMax <= 0) {\n throw new Error(`iso-ssbg1-v1 block horizon needs a positive maximum K (got ${kMax})`)\n }\n return serial32Precedes(a, b) ? Math.floor(serial32Distance(a, b) / kMax) : 0\n}\n", "/**\n * IP address literals as draft-ramadan-moq-multicast-02 7.2.6 requires them of\n * `sourceAddress`, `groupAddress` and `senderAddress`: the IPv4address or\n * IPv6address form of RFC 3986 3.2.2. Dotted decimal with no leading zero in\n * any dec-octet (the ls32 tail of an IPv6 literal included); IPv6 h16 groups\n * may carry leading zeros in either hex case; no brackets, no zone identifier,\n * never a name. Draft: Blockcast/moqcast-draft@5f49172.\n *\n * Addresses compare as IP addresses, never as strings, and an address of one\n * family never matches one of the other: an IPv4-mapped IPv6 address is of the\n * IPv6 family, so it parses to 16 octets and never equals its IPv4 address.\n */\n\nconst DEC_OCTET = /^(?:0|[1-9]\\d{0,2})$/;\nconst H16 = /^[0-9A-Fa-f]{1,4}$/;\n\nfunction parseIpv4(text: string): number[] | undefined {\n\tconst fields = text.split(\".\");\n\tif (fields.length !== 4) return undefined;\n\tconst octets: number[] = [];\n\tfor (const field of fields) {\n\t\tif (!DEC_OCTET.test(field)) return undefined;\n\t\tconst value = Number(field);\n\t\tif (value > 255) return undefined;\n\t\toctets.push(value);\n\t}\n\treturn octets;\n}\n\nfunction parseIpv6(text: string): Uint8Array | undefined {\n\tconst halves = text.split(\"::\");\n\tif (halves.length > 2) return undefined;\n\tconst groupsOf = (part: string) => (part === \"\" ? [] : part.split(\":\"));\n\tconst left = groupsOf(halves[0]!);\n\tconst right = halves.length === 2 ? groupsOf(halves[1]!) : [];\n\tconst words: number[][] = [];\n\tconst all = [...left, ...right];\n\t// The ls32 dotted tail ends the literal: the last group, never before \"::\".\n\tconst last = halves.length === 2 && right.length === 0 ? -1 : all.length - 1;\n\tfor (let index = 0; index < all.length; index++) {\n\t\tconst group = all[index]!;\n\t\tif (index === last && group.includes(\".\")) {\n\t\t\tconst v4 = parseIpv4(group);\n\t\t\tif (!v4) return undefined;\n\t\t\twords.push([(v4[0]! << 8) | v4[1]!, (v4[2]! << 8) | v4[3]!]);\n\t\t} else if (H16.test(group)) {\n\t\t\twords.push([Number.parseInt(group, 16)]);\n\t\t} else {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\tconst leftWords = words.slice(0, left.length).flat();\n\tconst rightWords = words.slice(left.length).flat();\n\tconst missing = 8 - leftWords.length - rightWords.length;\n\tif (halves.length === 2 ? missing < 1 : missing !== 0) return undefined;\n\tconst out = new Uint8Array(16);\n\t[...leftWords, ...new Array<number>(missing).fill(0), ...rightWords].forEach((word, index) => {\n\t\tout[index * 2] = word >> 8;\n\t\tout[index * 2 + 1] = word & 0xff;\n\t});\n\treturn out;\n}\n\n/** The octets of an RFC 3986 IPv4address (4) or IPv6address (16), or `undefined`. */\nexport function parseIpAddressLiteral(text: unknown): Uint8Array | undefined {\n\tif (typeof text !== \"string\") return undefined;\n\tif (text.includes(\":\")) return parseIpv6(text);\n\tconst v4 = parseIpv4(text);\n\treturn v4 ? Uint8Array.from(v4) : undefined;\n}\n\n/** Whether two address literals name one address of one family. Unparseable never matches. */\nexport function sameIpAddress(a: unknown, b: unknown): boolean {\n\tconst x = parseIpAddressLiteral(a);\n\tconst y = parseIpAddressLiteral(b);\n\treturn x !== undefined && y !== undefined && x.length === y.length && x.every((octet, index) => octet === y[index]);\n}\n\n/**\n * One spelling per address: dotted decimal for IPv4, RFC 5952 for IPv6\n * (lowercase, no leading zeros, the longest run of two or more zero groups,\n * leftmost on a tie, as \"::\"; an IPv4-mapped address in hex like any other).\n * `undefined` for text that is not an address literal.\n */\nexport function canonicalIpAddress(text: unknown): string | undefined {\n\tconst bytes = parseIpAddressLiteral(text);\n\tif (!bytes) return undefined;\n\tif (bytes.length === 4) return bytes.join(\".\");\n\tconst words = Array.from({ length: 8 }, (_, index) => (bytes[index * 2]! << 8) | bytes[index * 2 + 1]!);\n\tlet bestStart = -1;\n\tlet bestLength = 1;\n\tfor (let start = 0; start < 8; ) {\n\t\tlet end = start;\n\t\twhile (end < 8 && words[end] === 0) end++;\n\t\tif (end - start > bestLength) {\n\t\t\tbestStart = start;\n\t\t\tbestLength = end - start;\n\t\t}\n\t\tstart = end === start ? start + 1 : end;\n\t}\n\tconst hex = words.map(word => word.toString(16));\n\tif (bestStart < 0) return hex.join(\":\");\n\treturn `${hex.slice(0, bestStart).join(\":\")}::${hex.slice(bestStart + bestLength).join(\":\")}`;\n}\n", "/**\n * The draft-ramadan-moq-multicast-02 endpoint and `auth` rules, written once\n * for every receiver of the catalog: the receivers in `@blockcast/mmt-transport`\n * and the container resolver ignore every endpoint an issue names (all of\n * them when the issue is catalog-level, which leaves the broadcast no data).\n * `validateCmsfMmtpCatalog` reports none of them as a catalog fault, so MoQ\n * unicast stays usable. It lives here because mmt-base is the only package\n * every receiver depends on.\n *\n * Draft: Blockcast/moqcast-draft@5f49172 (draft-ramadan-moq-multicast-02 as\n * posted 2026-11-02), sections 4.1 and 7.2.6.\n */\n\nimport { parseIpAddressLiteral } from \"./ip-address.js\";\n\nexport type MulticastJoinMode = \"ssm\" | \"asm\";\n\n/** An endpoint's join mode and `S` (4.1): `sourceAddress` on SSM,\n * `auth.senderAddress` on protected ASM, absent on unprotected ASM. */\nexport interface MulticastEndpointSource {\n\tmode: MulticastJoinMode;\n\tsource?: string;\n}\n\nexport type MulticastCatalogIssueCode =\n\t| \"multicast_auth\"\n\t| \"multicast_endpoint\"\n\t| \"multicast_endpoint_auth\"\n\t| \"multicast_track\";\n\nexport interface MulticastCatalogIssue {\n\tcode: MulticastCatalogIssueCode;\n\t/** JSONPath of the offending field. */\n\tpath: string;\n\tmessage: string;\n\t/** Endpoint the issue makes a receiver ignore; absent for a catalog-level\n\t * issue, which leaves the receiver no endpoint at all (7.2.6). */\n\tendpointIndex?: number;\n}\n\n/** Canonical unpadded base64url of exactly 32 octets. */\nexport const ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;\n\nconst UINT32_MAX = 0xffff_ffff;\nconst AUTH_MEMBERS = new Set([\"scheme\", \"version\", \"sig\", \"keys\", \"minEpoch\"]);\nconst KEY_MEMBERS = new Set([\"kid\", \"publicKey\", \"firstEpoch\"]);\n// Draft -01's separate record port is no member here: a leftover one is an\n// unknown member, so the endpoint is ignored (libmmt policy, fails closed).\nconst ENDPOINT_AUTH_MEMBERS = new Set([\n\t\"streamId\",\n\t\"senderAddress\",\n\t\"maxAuthDelayMs\",\n\t\"manifestTimerMs\",\n\t\"manifestTrack\",\n]);\n\ntype JsonObject = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isIntegerIn(value: unknown, min: number, max: number): value is number {\n\treturn Number.isSafeInteger(value) && (value as number) >= min && (value as number) <= max;\n}\n\n/** Defaults of an endpoint's `auth.maxAuthDelayMs` and `auth.manifestTimerMs` (7.2.6). */\nexport const BCP_DEFAULT_MAX_AUTH_DELAY_MS = 200;\nexport const BCP_DEFAULT_MANIFEST_TIMER_MS = 20;\n\n/** The timing and mirror members of an endpoint's `auth` as a receiver uses them. */\nexport interface EndpointAuthTiming {\n\tmaxAuthDelayMs: number;\n\tmanifestTimerMs: number;\n\tmanifestTrack?: string;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n\n/**\n * 7.2.6: a `maxAuthDelayMs` outside 50..1000 is clamped to the nearer bound;\n * one that is not a JSON number, or an in-range one that is not an integer,\n * is absent (200). A `manifestTimerMs` that is not a JSON number, or an\n * integer-free value of at least 1, is absent (20); one below 1 makes the\n * endpoint ignored, so it is returned here as is for the caller to refuse. A\n * `manifestTrack` that is not a string is absent.\n */\nexport function endpointAuthTiming(auth: unknown): EndpointAuthTiming {\n\tconst record = isObject(auth) ? auth : {};\n\tconst delay = record[\"maxAuthDelayMs\"];\n\tconst timer = record[\"manifestTimerMs\"];\n\tconst track = record[\"manifestTrack\"];\n\tconst maxAuthDelayMs = !isFiniteNumber(delay)\n\t\t? BCP_DEFAULT_MAX_AUTH_DELAY_MS\n\t\t: delay < 50 ? 50 : delay > 1000 ? 1000 : Number.isInteger(delay) ? delay : BCP_DEFAULT_MAX_AUTH_DELAY_MS;\n\tconst manifestTimerMs = !isFiniteNumber(timer)\n\t\t? BCP_DEFAULT_MANIFEST_TIMER_MS\n\t\t: timer < 1 || Number.isSafeInteger(timer) ? timer : BCP_DEFAULT_MANIFEST_TIMER_MS;\n\treturn {\n\t\tmaxAuthDelayMs,\n\t\tmanifestTimerMs,\n\t\t...(typeof track === \"string\" && track.length > 0 ? { manifestTrack: track } : {}),\n\t};\n}\n\n/**\n * Join mode and `S` of one endpoint, or `undefined` when its fields disagree\n * and a receiver must ignore it (4.1). `sourceAddress` alone selects the mode;\n * `protocol` may only restate it, so \"amt\" and \"atsc3\" (draft -00) are\n * inconsistent here: a bearer is a `networkSource`, never a protocol.\n */\nexport function multicastEndpointSource(endpoint: unknown): MulticastEndpointSource | undefined {\n\tif (!isObject(endpoint)) return undefined;\n\tconst sourceAddress = endpoint[\"sourceAddress\"];\n\tif (sourceAddress !== undefined && !parseIpAddressLiteral(sourceAddress)) return undefined;\n\tconst mode: MulticastJoinMode = sourceAddress === undefined ? \"asm\" : \"ssm\";\n\tconst protocol = endpoint[\"protocol\"];\n\tif (protocol !== undefined && protocol !== mode) return undefined;\n\tconst auth = endpoint[\"auth\"];\n\tconst senderAddress = isObject(auth) ? auth[\"senderAddress\"] : undefined;\n\tif (mode === \"ssm\") return senderAddress === undefined ? { mode, source: sourceAddress as string } : undefined;\n\tif (auth === undefined) return { mode };\n\treturn parseIpAddressLiteral(senderAddress) ? { mode, source: senderAddress as string } : undefined;\n}\n\nfunction authIssues(auth: unknown): MulticastCatalogIssue[] {\n\tconst base = \"$.multicast.auth\";\n\tconst issue = (path: string, message: string): MulticastCatalogIssue[] => [\n\t\t{ code: \"multicast_auth\", path: `${base}${path}`, message },\n\t];\n\tif (!isObject(auth)) return issue(\"\", \"auth must be an object\");\n\tconst unknown = Object.keys(auth).filter(key => !AUTH_MEMBERS.has(key));\n\tif (unknown.length > 0) {\n\t\treturn issue(\"\", `auth has unknown member(s) ${unknown.join(\", \")}; draft -02 7.2.6 defines scheme, version, sig, keys and minEpoch`);\n\t}\n\tif (auth[\"scheme\"] !== \"bc-provenance\") return issue(\".scheme\", 'scheme must be \"bc-provenance\"');\n\tif (auth[\"version\"] !== 2) return issue(\".version\", \"version must be 2 (bc-provenance version 2, draft -02 7.2)\");\n\tif (auth[\"sig\"] !== \"ed25519\") return issue(\".sig\", 'sig must be \"ed25519\"');\n\tconst keys = auth[\"keys\"];\n\tif (!Array.isArray(keys) || keys.length < 1 || keys.length > 4) {\n\t\treturn issue(\".keys\", \"keys must be an array of one to four {kid, publicKey, firstEpoch} objects\");\n\t}\n\tconst kids = new Set<number>();\n\tconst firstEpochs = new Set<number>();\n\tfor (let index = 0; index < keys.length; index++) {\n\t\tconst key = keys[index];\n\t\tconst at = `.keys[${index}]`;\n\t\tif (!isObject(key) || Object.keys(key).some(member => !KEY_MEMBERS.has(member))) {\n\t\t\treturn issue(at, \"a key is exactly {kid, publicKey, firstEpoch}\");\n\t\t}\n\t\tif (!isIntegerIn(key[\"kid\"], 0, UINT32_MAX)) return issue(`${at}.kid`, \"kid must be an unsigned 32-bit integer\");\n\t\tif (typeof key[\"publicKey\"] !== \"string\" || !ED25519_PUBLIC_KEY_BASE64URL.test(key[\"publicKey\"])) {\n\t\t\treturn issue(`${at}.publicKey`, \"publicKey must be a 32-octet Ed25519 key in canonical unpadded base64url\");\n\t\t}\n\t\tif (!isIntegerIn(key[\"firstEpoch\"], 0, UINT32_MAX)) {\n\t\t\treturn issue(`${at}.firstEpoch`, \"firstEpoch must be an unsigned 32-bit integer\");\n\t\t}\n\t\t// A repeated kid or firstEpoch leaves no key set (7.2.6): fail the whole\n\t\t// block, never fall back to unauthenticated reception.\n\t\tif (kids.has(key[\"kid\"])) return issue(`${at}.kid`, `kid ${key[\"kid\"]} is repeated; the catalog holds no key set`);\n\t\tif (firstEpochs.has(key[\"firstEpoch\"])) {\n\t\t\treturn issue(`${at}.firstEpoch`, `firstEpoch ${key[\"firstEpoch\"]} is repeated; the catalog holds no key set`);\n\t\t}\n\t\tkids.add(key[\"kid\"]);\n\t\tfirstEpochs.add(key[\"firstEpoch\"]);\n\t}\n\tif (!isIntegerIn(auth[\"minEpoch\"], 0, UINT32_MAX)) {\n\t\treturn issue(\".minEpoch\", \"minEpoch must be an unsigned 32-bit integer\");\n\t}\n\treturn [];\n}\n\nfunction endpointAuthIssue(\n\tendpoint: JsonObject,\n\tmode: MulticastJoinMode,\n\tat: string,\n): { path: string; message: string } | undefined {\n\tconst auth = endpoint[\"auth\"];\n\tif (!isObject(auth)) return { path: `${at}.auth`, message: \"auth must be an object\" };\n\tconst unknown = Object.keys(auth).filter(key => !ENDPOINT_AUTH_MEMBERS.has(key));\n\tif (unknown.length > 0) return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(\", \")}` };\n\tif (!isIntegerIn(auth[\"streamId\"], 0, UINT32_MAX)) {\n\t\treturn { path: `${at}.auth.streamId`, message: \"streamId must be an unsigned 32-bit integer\" };\n\t}\n\tif (mode === \"ssm\" && auth[\"senderAddress\"] !== undefined) {\n\t\treturn { path: `${at}.auth.senderAddress`, message: \"senderAddress must not appear on an SSM endpoint; S is sourceAddress\" };\n\t}\n\tif (mode === \"asm\" && !parseIpAddressLiteral(auth[\"senderAddress\"])) {\n\t\treturn { path: `${at}.auth.senderAddress`, message: \"a protected ASM endpoint requires senderAddress, an RFC 3986 IP address literal\" };\n\t}\n\t// maxAuthDelayMs never ignores the endpoint (clamped or absent) and\n\t// manifestTrack is absent when it is not a string: endpointAuthTiming.\n\tif (endpointAuthTiming(auth).manifestTimerMs < 1) {\n\t\treturn { path: `${at}.auth.manifestTimerMs`, message: \"manifestTimerMs must be at least 1\" };\n\t}\n\tif (!isIntegerIn(endpoint[\"bandwidth\"], 1, Number.MAX_SAFE_INTEGER)) {\n\t\treturn { path: `${at}.bandwidth`, message: \"a protected endpoint requires bandwidth, which bounds the receiver's hold queues\" };\n\t}\n\treturn undefined;\n}\n\n/**\n * Where a catalog object carries the namespace of the catalog track it was\n * read from. A symbol, so it is never wire data and survives object spread;\n * `Symbol.for`, so two bundled copies of this module agree on it.\n */\nexport const CATALOG_NAMESPACE: unique symbol = Symbol.for(\"@blockcast/mmt-base/catalogNamespace\");\n\n/**\n * `catalog` tagged with the namespace of its catalog track. Every receiver\n * rule that takes the catalog (`multicastCatalogIssues`,\n * `admittedMulticastEndpoints` and the transport selectors built on them)\n * then judges the foreign-namespace rule against it, independent of track\n * order. Tag once, where the catalog track is read.\n */\nexport function withCatalogNamespace<T extends object>(catalog: T, namespace: string): T {\n\treturn { ...catalog, [CATALOG_NAMESPACE]: namespace };\n}\n\n/**\n * Every draft -02 4.1 / 7.2.6 violation in `catalog.multicast`, in document\n * order. Packet-id, group-address and track-existence rules are not here; the\n * container validator and the receivers already check those.\n *\n * `catalogNamespace` is the namespace of the catalog track, from `options` or else the\n * catalog's `withCatalogNamespace` tag. Without it the\n * foreign-namespace rule can only compare listed tracks with each other: the\n * first listed track's namespace becomes the reference, so which side of a\n * disagreement is dropped depends on order. It still fails closed; pass\n * `catalogNamespace` wherever the catalog track is known.\n */\nexport function multicastCatalogIssues(\n\tcatalog: unknown,\n\toptions: { catalogNamespace?: string } = {},\n): MulticastCatalogIssue[] {\n\tif (!isObject(catalog)) return [];\n\tconst issues: MulticastCatalogIssue[] = [];\n\tif (\"multicast.auth\" in catalog) {\n\t\t// Fail closed: a draft -00 publisher meant the flow to be protected, and\n\t\t// the key it names verifies nothing in bc-provenance version 2.\n\t\tissues.push({\n\t\t\tcode: \"multicast_auth\",\n\t\t\tpath: '$[\"multicast.auth\"]',\n\t\t\tmessage: 'the root \"multicast.auth\" key is retired; declare auth as multicast.auth (draft -02 7.2.6)',\n\t\t});\n\t}\n\tconst multicast = catalog[\"multicast\"];\n\tif (!isObject(multicast)) return issues;\n\tconst protectedCatalog = multicast[\"auth\"] !== undefined;\n\tif (protectedCatalog) issues.push(...authIssues(multicast[\"auth\"]));\n\tconst endpoints = multicast[\"endpoints\"];\n\tif (!Array.isArray(endpoints)) return issues;\n\n\tconst tracks = Array.isArray(catalog[\"tracks\"]) ? catalog[\"tracks\"] : [];\n\tconst namespaceOf = new Map<string, unknown>();\n\tfor (const track of tracks) {\n\t\tif (isObject(track) && typeof track[\"name\"] === \"string\") namespaceOf.set(track[\"name\"], track[\"namespace\"]);\n\t}\n\tconst tagged = (catalog as { [CATALOG_NAMESPACE]?: unknown })[CATALOG_NAMESPACE];\n\tlet referenceNamespace = options.catalogNamespace ?? (typeof tagged === \"string\" ? tagged : undefined);\n\n\tconst sources: Array<MulticastEndpointSource | undefined> = [];\n\tfor (let index = 0; index < endpoints.length; index++) {\n\t\tconst endpoint = endpoints[index];\n\t\tconst at = `$.multicast.endpoints[${index}]`;\n\t\tconst fail = (code: MulticastCatalogIssueCode, path: string, message: string) => {\n\t\t\tissues.push({ code, path, message, endpointIndex: index });\n\t\t};\n\t\tsources.push(undefined);\n\t\tif (!isObject(endpoint)) {\n\t\t\tfail(\"multicast_endpoint\", at, \"endpoint must be an object\");\n\t\t\tcontinue;\n\t\t}\n\t\tconst protocol = endpoint[\"protocol\"];\n\t\tif (protocol !== undefined && protocol !== \"ssm\" && protocol !== \"asm\") {\n\t\t\tfail(\"multicast_endpoint\", `${at}.protocol`, `protocol must be \"ssm\" or \"asm\"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -02 4.2)`);\n\t\t\tcontinue;\n\t\t}\n\t\tconst hasAuth = endpoint[\"auth\"] !== undefined;\n\t\tif (protectedCatalog !== hasAuth) {\n\t\t\tfail(\n\t\t\t\t\"multicast_endpoint_auth\",\n\t\t\t\t`${at}.auth`,\n\t\t\t\tprotectedCatalog\n\t\t\t\t\t? \"every endpoint requires auth when multicast carries auth; a receiver ignores this endpoint\"\n\t\t\t\t\t: \"auth names no key without multicast.auth; a receiver ignores this endpoint\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\t// 7.2.6: addresses are RFC 3986 IP literals, never names; 4.1: S and G\n\t\t// of one family, or the endpoint's fields are mutually inconsistent.\n\t\tconst sourceAddress = endpoint[\"sourceAddress\"];\n\t\tconst sourceBytes = sourceAddress === undefined ? undefined : parseIpAddressLiteral(sourceAddress);\n\t\tif (sourceAddress !== undefined && !sourceBytes) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.sourceAddress`, \"sourceAddress must be an RFC 3986 IPv4address or IPv6address literal\");\n\t\t\tcontinue;\n\t\t}\n\t\tconst groupBytes = parseIpAddressLiteral(endpoint[\"groupAddress\"]);\n\t\tif (!groupBytes) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.groupAddress`, \"groupAddress must be an RFC 3986 IPv4address or IPv6address literal\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (sourceBytes && sourceBytes.length !== groupBytes.length) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.sourceAddress`, \"sourceAddress and groupAddress are of different address families\");\n\t\t\tcontinue;\n\t\t}\n\t\tconst mode: MulticastJoinMode = sourceAddress === undefined ? \"asm\" : \"ssm\";\n\t\tif (protocol !== undefined && protocol !== mode) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.protocol`, `protocol ${JSON.stringify(protocol)} disagrees with sourceAddress, which selects ${mode}`);\n\t\t\tcontinue;\n\t\t}\n\t\t// The (S, G, port) rule below compares ports with ===, so only an\n\t\t// integer may reach it: \"5001\" and 5001 would otherwise both pass.\n\t\tif (!isIntegerIn(endpoint[\"port\"], 1, 65535)) {\n\t\t\tfail(\"multicast_endpoint\", `${at}.port`, \"port must be an integer from 1 to 65535\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (hasAuth) {\n\t\t\tconst authIssue = endpointAuthIssue(endpoint, mode, at);\n\t\t\tif (authIssue) {\n\t\t\t\tfail(\"multicast_endpoint_auth\", authIssue.path, authIssue.message);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tconst source = multicastEndpointSource(endpoint);\n\t\tconst senderBytes = source?.mode === \"asm\" && source.source !== undefined ? parseIpAddressLiteral(source.source) : undefined;\n\t\tif (senderBytes && senderBytes.length !== groupBytes.length) {\n\t\t\tfail(\"multicast_endpoint_auth\", `${at}.auth.senderAddress`, \"senderAddress and groupAddress are of different address families\");\n\t\t\tcontinue;\n\t\t}\n\t\tsources[index] = source;\n\n\t\tconst refs = Array.isArray(endpoint[\"tracks\"]) ? endpoint[\"tracks\"] : [];\n\t\tfor (let refIndex = 0; refIndex < refs.length; refIndex++) {\n\t\t\tconst ref = refs[refIndex];\n\t\t\tconst name = isObject(ref) ? ref[\"name\"] : ref;\n\t\t\tif (typeof name !== \"string\") continue;\n\t\t\tconst namespace = namespaceOf.get(name);\n\t\t\tif (typeof namespace !== \"string\") continue;\n\t\t\treferenceNamespace ??= namespace;\n\t\t\tif (namespace !== referenceNamespace) {\n\t\t\t\tfail(\"multicast_track\", `${at}.tracks[${refIndex}].name`, `track ${JSON.stringify(name)} declares namespace ${JSON.stringify(namespace)}, not the catalog track's ${JSON.stringify(referenceNamespace)}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t// One endpoint object per (S, G, port) (4.1), and every protected endpoint\n\t// sharing its (S, G, port) with another is ignored (7.2.6), so both sides\n\t// are named. The key is the parsed addresses, never their spelling: an\n\t// address of one family never matches the other (an IPv4-mapped address\n\t// is IPv6), and \"232.0.0.1\" and \"ff3e::1\" never collide with each other.\n\tconst addressKey = (text: unknown) => {\n\t\tconst bytes = text === undefined ? undefined : parseIpAddressLiteral(text);\n\t\treturn bytes ? `${bytes.length}:${bytes.join(\".\")}` : \"-\";\n\t};\n\tconst tupleOf = (index: number) => {\n\t\tconst endpoint = endpoints[index] as JsonObject;\n\t\treturn `${addressKey(sources[index]!.source)}|${addressKey(endpoint[\"groupAddress\"])}|${endpoint[\"port\"]}`;\n\t};\n\tfor (let index = 0; index < endpoints.length; index++) {\n\t\tif (!sources[index]) continue;\n\t\tconst own = tupleOf(index);\n\t\tfor (let other = 0; other < endpoints.length; other++) {\n\t\t\tif (other === index || !sources[other] || tupleOf(other) !== own) continue;\n\t\t\tissues.push({ code: \"multicast_endpoint\", path: `$.multicast.endpoints[${index}].port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn issues;\n}\n\n/**\n * Indices of the endpoints a receiver may use: none when a catalog-level\n * issue leaves it no key set (7.2.6), otherwise every endpoint no issue\n * names. Other receiver checks (group address, packet ids) still apply.\n */\nexport function admittedMulticastEndpoints(\n\tcatalog: unknown,\n\toptions: { catalogNamespace?: string } = {},\n): ReadonlySet<number> {\n\tconst multicast = isObject(catalog) ? catalog[\"multicast\"] : undefined;\n\tconst endpoints = isObject(multicast) && Array.isArray(multicast[\"endpoints\"]) ? multicast[\"endpoints\"] : [];\n\tconst issues = multicastCatalogIssues(catalog, options);\n\tif (issues.some(issue => issue.endpointIndex === undefined)) return new Set();\n\tconst ignored = new Set(issues.map(issue => issue.endpointIndex));\n\treturn new Set(endpoints.map((_, index) => index).filter(index => !ignored.has(index)));\n}\n", "/**\n * Runtime FEC bindings (BLO-50171): what each source track's FEC resolves to\n * for this library's receiver, read from `validateFecCatalog`'s per-instance\n * result and never re-derived.\n *\n * Every runtime reader (the container's `resolveCatalogTrackFec`,\n * `resolveRepairLayers`, `MulticastManager.configureMultiTrack`, transport's\n * `catalogRepairTracks`) goes through `resolveFecBindings`, so they cannot\n * disagree with the validator or with each other about which tracks have FEC\n * and which repair tracks belong to them.\n *\n * The draft-ramadan-moq-fec-catalog-00 array is the only shape. Both catalog\n * dialects bind through it: an MSF `tracks[]` entry directly, and a hang\n * `video`/`audio` rendition (`fec` the same array) with its `repair.renditions`\n * entries (`source` + `instanceId`), mapped onto a `tracks[]` view so the same\n * validator runs. A hang rendition is authoritative over a `tracks[]` entry of\n * the same name.\n *\n * Fail closed per instance: a source whose base instance is not `ok` resolves\n * to `unavailable` with the validator's code and path, and costs no other\n * track anything. A draft-01 object `fec` is `fec_member` at that track's\n * `fec`: the track plays without FEC.\n */\nimport { validateFecCatalog, type FecCatalogCode, type FecCatalogReceiver } from './fec-catalog.js'\n\n/** The receiver this runtime is: it decodes `iso-ssbg1-v1` only. */\nexport const RUNTIME_FEC_RECEIVER: FecCatalogReceiver = Object.freeze({\n schemes: Object.freeze(['raptorq', 'reed-solomon']),\n blockProfiles: Object.freeze(['iso-ssbg1-v1']),\n overlays: true,\n})\n\n/** Repair track priority when the track states none (MoQT scale, 192..255). */\nexport const DEFAULT_FEC_REPAIR_PRIORITY = 240\n\n/** One `ok` instance's geometry. */\nexport interface FecInstanceGeometry {\n readonly instanceId: number\n readonly scheme: 'raptorq' | 'reed-solomon'\n /** `iso-ssbg1-v1` under `RUNTIME_FEC_RECEIVER`. */\n readonly blockProfile: string\n /** K. */\n readonly sourceSymbols: number\n /** P_0 for a base instance; the overlay's own P for an overlay. */\n readonly repairSymbols: number\n /** T. */\n readonly symbolSize: number\n readonly interleaveDepthMs?: number\n readonly reorderToleranceMs?: number\n readonly repairTrack: string\n readonly symbolTrack?: string\n}\n\nexport interface FecLayerBinding {\n readonly layer: number\n readonly repairTrackName: string\n readonly repairSymbols: number\n /** O_i = sum(P_j, j < i). */\n readonly rsIdOffset: number\n readonly priority: number\n}\n\nexport interface FecOverlayBinding {\n readonly repairTrackName: string\n readonly scope: 'keyframe'\n readonly sourceSymbols: number\n readonly repairSymbols: number\n readonly priority: number\n readonly geometry: FecInstanceGeometry\n}\n\nexport interface FecBindingUnavailable {\n /** The validator's code, or `fec_base_instances` for a track with no base\n * instance or more than one `ok` one (the MMTP wire carries one base Source FEC Payload\n * ID, mmt-02 7.4.1). */\n readonly code: FecCatalogCode | 'fec_base_instances'\n /** JSON Pointer into the catalog (a hang path starts `/video` or `/audio`). */\n readonly path: string\n readonly outcome: 'unavailable' | 'violation'\n}\n\nexport type FecSourceBinding =\n | {\n readonly status: 'ok'\n readonly track: string\n /** JSON Pointer of the source track (`/tracks/<i>`, `/video/renditions/<name>`). */\n readonly pointer: string\n readonly base: FecInstanceGeometry\n /** Base layers in order, layer 0 first. */\n readonly layers: readonly FecLayerBinding[]\n /** Sum of P over the base layers: the wire RSB_length. */\n readonly totalBaseRepairSymbols: number\n readonly overlay?: FecOverlayBinding\n /** Why a declared overlay is not used, when one is not. */\n readonly overlayUnavailable?: FecBindingUnavailable\n }\n | {\n readonly status: 'unavailable'\n readonly track: string\n readonly pointer: string\n readonly reason: FecBindingUnavailable\n }\n\ntype Obj = Record<string, unknown>\nconst recordOf = (v: unknown): Obj | undefined =>\n typeof v === 'object' && v !== null && !Array.isArray(v) ? (v as Obj) : undefined\nconst escape = (s: string) => s.replace(/~/g, '~0').replace(/\\//g, '~1')\n\n/**\n * The `tracks[]` view of a catalog in either dialect, with each view track's\n * JSON Pointer in the original catalog.\n */\nexport function fecCatalogTracks(catalog: unknown): { tracks: unknown[]; pointers: string[] } {\n const root = recordOf(catalog) ?? {}\n const tracks: unknown[] = []\n const pointers: string[] = []\n const hangNames = new Set<string>()\n for (const role of ['video', 'audio'] as const) {\n for (const [name, r] of Object.entries(recordOf(recordOf(root[role])?.['renditions']) ?? {})) {\n const entry = recordOf(r)\n if (!entry) continue\n hangNames.add(name)\n const kind = recordOf(entry['container'])?.['kind']\n // A hang rendition carries no `packaging`; its container kind is it.\n // `mmtp-fec` is hang's kind for MMTP that carries FEC (the FFmpeg\n // publisher's `moq_publish_set_video_packaging`): MMTP packaging.\n const view: Obj = { name, packaging: kind === undefined || kind === 'mmtp-fec' ? 'mmtp' : kind }\n if (entry['fec'] !== undefined) view['fec'] = entry['fec']\n tracks.push(view)\n pointers.push(`/${role}/renditions/${escape(name)}`)\n }\n }\n const hangRenditions = tracks.length\n for (const [name, r] of Object.entries(recordOf(recordOf(root['repair'])?.['renditions']) ?? {})) {\n hangNames.add(name)\n tracks.push({ packaging: 'fec-repair', ...recordOf(r), name })\n pointers.push(`/repair/renditions/${escape(name)}`)\n }\n const raw = Array.isArray(root['tracks']) ? root['tracks'] : []\n const named = new Set(hangNames)\n raw.forEach((t, i) => {\n const name = recordOf(t)?.['name']\n if (typeof name === 'string') named.add(name)\n if (typeof name === 'string' && hangNames.has(name)) return\n tracks.push(t)\n pointers.push(`/tracks/${i}`)\n })\n // hang layer 0 needs no `repair.renditions` entry: the rendition's instance\n // names it, and a hang Root (hang's toHang) carries no repair block. Such a\n // name stands for a repair track of that instance.\n tracks.slice(0, hangRenditions).forEach((t, ti) => {\n const view = t as Obj\n if (!Array.isArray(view['fec'])) return\n view['fec'].forEach((o, ii) => {\n const repairTrack = recordOf(o)?.['repairTrack']\n if (typeof repairTrack !== 'string' || named.has(repairTrack)) return\n named.add(repairTrack)\n const instanceId = recordOf(o)?.['instanceId']\n tracks.push({ name: repairTrack, packaging: 'fec-repair', source: view['name'], ...(instanceId === undefined ? {} : { instanceId }) })\n pointers.push(`${pointers[ti]}/fec/${ii}/repairTrack`)\n })\n })\n return { tracks, pointers }\n}\n\n/**\n * Every source track's runtime FEC binding, by track name. A track with no\n * `fec`, or an empty `fec` array, declares no FEC and has no entry.\n */\nexport function resolveFecBindings(\n catalog: unknown,\n receiver: FecCatalogReceiver = RUNTIME_FEC_RECEIVER,\n): ReadonlyMap<string, FecSourceBinding> {\n const { tracks, pointers } = fecCatalogTracks(catalog)\n const report = validateFecCatalog({ tracks }, receiver)\n const at = (path: string) => {\n const m = /^\\/tracks\\/(\\d+)(.*)$/.exec(path)\n return m ? pointers[Number(m[1])] + m[2] : path\n }\n const trackAt = (i: number) => recordOf(tracks[i]) ?? {}\n const nameAt = (i: number | null) => (i === null ? undefined : (trackAt(i)['name'] as string))\n const why = (i: { outcome: string; issues: readonly { code: FecCatalogCode; path: string }[]; path: string }) =>\n Object.freeze({\n code: i.issues[0]?.code ?? 'fec_member',\n path: at(i.issues[0]?.path ?? i.path),\n outcome: i.outcome === 'unavailable' ? ('unavailable' as const) : ('violation' as const),\n })\n\n const out = new Map<string, FecSourceBinding>()\n tracks.forEach((t, ti) => {\n const track = recordOf(t)\n const name = track?.['name']\n if (!track || typeof name !== 'string' || out.has(name)) return\n if (track['packaging'] === 'fec-repair' || track['packaging'] === 'fec-source') return\n const fec = track['fec']\n if (fec === undefined) return\n if (!Array.isArray(fec)) {\n const reason = { code: 'fec_member' as const, path: at(`/tracks/${ti}/fec`), outcome: 'violation' as const }\n out.set(name, Object.freeze({ status: 'unavailable', track: name, pointer: pointers[ti]!, reason: Object.freeze(reason) }))\n return\n }\n if (fec.length === 0) return\n const insts = report.instances.filter((i) => i.trackIndex === ti)\n const isOverlay = (i: (typeof insts)[number]) => Object.hasOwn(recordOf(fec[Number(i.path.split('/').pop())]) ?? {}, 'scope')\n const bases = insts.filter((i) => !isOverlay(i))\n const okBases = bases.filter((i) => i.outcome === 'ok')\n if (okBases.length !== 1) {\n const reason =\n // No base instance at all (overlay-only `fec`) is a count of 0, not\n // the overlay's own issue.\n okBases.length > 1 || bases.length === 0\n ? Object.freeze({ code: 'fec_base_instances' as const, path: at(`/tracks/${ti}/fec`), outcome: 'violation' as const })\n : why(bases[0] ?? insts[0]!)\n out.set(name, Object.freeze({ status: 'unavailable', track: name, pointer: pointers[ti]!, reason }))\n return\n }\n const b = okBases[0]!\n const base = geometry(recordOf(fec[Number(b.path.split('/').pop())])!, b.instanceId!, nameAt(b.symbolTrack))\n const priorityOf = (i: number | null) => {\n const p = i === null ? undefined : trackAt(i)['priority']\n return typeof p === 'number' ? p : DEFAULT_FEC_REPAIR_PRIORITY\n }\n const layers: FecLayerBinding[] = [\n { layer: 0, repairTrackName: base.repairTrack, repairSymbols: base.repairSymbols, rsIdOffset: 0, priority: priorityOf(b.repairTrack) },\n ]\n const more = b.boundRepairTracks\n .map((tj) => ({ tj, layer: trackAt(tj)['repairLayer'] as number }))\n .sort((x, y) => x.layer - y.layer)\n let offset = base.repairSymbols\n for (const { tj, layer } of more) {\n const repairSymbols = trackAt(tj)['repairSymbols'] as number\n layers.push({ layer, repairTrackName: nameAt(tj)!, repairSymbols, rsIdOffset: offset, priority: priorityOf(tj) })\n offset += repairSymbols\n }\n let overlay: FecOverlayBinding | undefined\n let overlayUnavailable: FecBindingUnavailable | undefined\n const ov = insts.find(isOverlay)\n if (ov?.outcome === 'ok') {\n const g = geometry(recordOf(fec[Number(ov.path.split('/').pop())])!, ov.instanceId!, nameAt(ov.symbolTrack))\n overlay = Object.freeze({\n repairTrackName: g.repairTrack,\n scope: 'keyframe' as const,\n sourceSymbols: g.sourceSymbols,\n repairSymbols: g.repairSymbols,\n priority: priorityOf(ov.repairTrack),\n geometry: g,\n })\n } else if (ov) overlayUnavailable = why(ov)\n out.set(\n name,\n Object.freeze({\n status: 'ok' as const,\n track: name,\n pointer: pointers[ti]!,\n base,\n layers: Object.freeze(layers.map((l) => Object.freeze(l))),\n totalBaseRepairSymbols: offset,\n ...(overlay ? { overlay } : {}),\n ...(overlayUnavailable ? { overlayUnavailable } : {}),\n }),\n )\n })\n return out\n}\n\n// The validator passed this instance for this receiver, so every member read\n// here is present and in range.\nfunction geometry(o: Obj, instanceId: number, symbolTrack: string | undefined): FecInstanceGeometry {\n return Object.freeze({\n instanceId,\n scheme: o['scheme'] as 'raptorq' | 'reed-solomon',\n blockProfile: o['blockProfile'] as string,\n sourceSymbols: o['sourceSymbols'] as number,\n repairSymbols: o['repairSymbols'] as number,\n symbolSize: o['symbolSize'] as number,\n ...(typeof o['interleaveDepthMs'] === 'number' ? { interleaveDepthMs: o['interleaveDepthMs'] } : {}),\n ...(typeof o['reorderToleranceMs'] === 'number' ? { reorderToleranceMs: o['reorderToleranceMs'] } : {}),\n repairTrack: o['repairTrack'] as string,\n ...(symbolTrack !== undefined ? { symbolTrack } : {}),\n })\n}\n", "/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n\n// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.\n// node.js versions earlier than v19 don't declare it in global scope.\n// For node.js, package.json#exports field mapping rewrites import\n// from `crypto` to `cryptoNode`, which imports native module.\n// Makes the utils un-importable in browsers without a bundler.\n// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.\nimport { crypto } from '@noble/hashes/crypto';\n\n/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */\nexport function isBytes(a: unknown): a is Uint8Array {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n\n/** Asserts something is positive integer. */\nexport function anumber(n: number): void {\n if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n);\n}\n\n/** Asserts something is Uint8Array. */\nexport function abytes(b: Uint8Array | undefined, ...lengths: number[]): void {\n if (!isBytes(b)) throw new Error('Uint8Array expected');\n if (lengths.length > 0 && !lengths.includes(b.length))\n throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);\n}\n\n/** Asserts something is hash */\nexport function ahash(h: IHash): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new Error('Hash should be wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n}\n\n/** Asserts a hash instance has not been destroyed / finished */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/** Asserts output is properly-sized byte array */\nexport function aoutput(out: any, instance: any): void {\n abytes(out);\n const min = instance.outputLen;\n if (out.length < min) {\n throw new Error('digestInto() expects output buffer of length at least ' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/** Cast u8 / u16 / u32 to u8. */\nexport function u8(arr: TypedArray): Uint8Array {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** Cast u8 / u16 / u32 to u32. */\nexport function u32(arr: TypedArray): Uint32Array {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n\n/** Zeroize a byte array. Warning: JS provides no guarantees. */\nexport function clean(...arrays: TypedArray[]): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/** Create DataView of an array for easy byte-level manipulation. */\nexport function createView(arr: TypedArray): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** The rotate right (circular right shift) operation for uint32 */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/** The rotate left (circular left shift) operation for uint32 */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Is current platform little-endian? Most are. Big-Endian platform: IBM */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/** The byte swap operation for uint32 */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/** Conditionally byte swap if on a big-endian platform */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n);\n\n/** @deprecated */\nexport const byteSwapIfBE: typeof swap8IfBE = swap8IfBE;\n/** In place byte swap for Uint32Array */\nexport function byteSwap32(arr: Uint32Array): Uint32Array {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n\nexport const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE\n ? (u: Uint32Array) => u\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string. Uses built-in function, when available.\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex: string): Uint8Array {\n if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);\n // @ts-ignore\n if (hasHexBuiltin) return Uint8Array.fromHex(hex);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new Error('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new Error('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * Call of async fn will return Promise, which will be fullfiled only on\n * next scheduler queue processing step and this is exactly what we need.\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/** Returns control to thread each 'tick' ms to avoid blocking. */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\ndeclare const TextDecoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])\n */\nexport function utf8ToBytes(str: string): Uint8Array {\n if (typeof str !== 'string') throw new Error('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/**\n * Converts bytes to string using UTF8 encoding.\n * @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'\n */\nexport function bytesToUtf8(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\n/** Accepted input of hash functions. Strings are converted to byte arrays. */\nexport type Input = string | Uint8Array;\n/**\n * Normalizes (non-hex) string or Uint8Array to Uint8Array.\n * Warning: when Uint8Array is passed, it would NOT get copied.\n * Keep in mind for future mutable operations.\n */\nexport function toBytes(data: Input): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n/**\n * Helper for KDFs: consumes uint8array or string.\n * When string is passed, does utf8 decoding, using TextDecoder.\n */\nexport function kdfInputToBytes(data: KDFInput): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** Copies several Uint8Arrays into one. */\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new Error('options should be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Hash interface. */\nexport type IHash = {\n (data: Uint8Array): Uint8Array;\n blockLen: number;\n outputLen: number;\n create: any;\n};\n\n/** For runtime check if class implements interface */\nexport abstract class Hash<T extends Hash<T>> {\n abstract blockLen: number; // Bytes per block\n abstract outputLen: number; // Bytes in output\n abstract update(buf: Input): this;\n // Writes digest into buf\n abstract digestInto(buf: Uint8Array): void;\n abstract digest(): Uint8Array;\n /**\n * Resets internal state. Makes Hash instance unusable.\n * Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed\n * by user, they will need to manually call `destroy()` when zeroing is necessary.\n */\n abstract destroy(): void;\n /**\n * Clones hash instance. Unsafe: doesn't check whether `to` is valid. Can be used as `clone()`\n * when no options are passed.\n * Reasons to use `_cloneInto` instead of clone: 1) performance 2) reuse instance => all internal\n * buffers are overwritten => causes buffer overwrite which is used for digest in some cases.\n * There are no guarantees for clean-up because it's impossible in JS.\n */\n abstract _cloneInto(to?: T): T;\n // Safe version that clones internal state\n abstract clone(): T;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream\n xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf\n};\n\n/** Hash function */\nexport type CHash = ReturnType<typeof createHasher>;\n/** Hash function with output */\nexport type CHashO = ReturnType<typeof createOptHasher>;\n/** XOF with output */\nexport type CHashXO = ReturnType<typeof createXOFer>;\n\n/** Wraps hash function, creating an interface on top of it */\nexport function createHasher<T extends Hash<T>>(\n hashCons: () => Hash<T>\n): {\n (msg: Input): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(): Hash<T>;\n} {\n const hashC = (msg: Input): Uint8Array => hashCons().update(toBytes(msg)).digest();\n const tmp = hashCons();\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = () => hashCons();\n return hashC;\n}\n\nexport function createOptHasher<H extends Hash<H>, T extends Object>(\n hashCons: (opts?: T) => Hash<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): Hash<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\n\nexport function createXOFer<H extends HashXOF<H>, T extends Object>(\n hashCons: (opts?: T) => HashXOF<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): HashXOF<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\nexport const wrapConstructor: typeof createHasher = createHasher;\nexport const wrapConstructorWithOpts: typeof createOptHasher = createOptHasher;\nexport const wrapXOFConstructorWithOpts: typeof createXOFer = createXOFer;\n\n/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */\nexport function randomBytes(bytesLength = 32): Uint8Array {\n if (crypto && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(bytesLength));\n }\n // Legacy Node.js compatibility\n if (crypto && typeof crypto.randomBytes === 'function') {\n return Uint8Array.from(crypto.randomBytes(bytesLength));\n }\n throw new Error('crypto.getRandomValues must be defined');\n}\n", "/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport { type Input, Hash, abytes, aexists, aoutput, clean, createView, toBytes } from './utils.ts';\n\n/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */\nexport function setBigUint64(\n view: DataView,\n byteOffset: number,\n value: bigint,\n isLE: boolean\n): void {\n if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);\n const _32n = BigInt(32);\n const _u32_max = BigInt(0xffffffff);\n const wh = Number((value >> _32n) & _u32_max);\n const wl = Number(value & _u32_max);\n const h = isLE ? 4 : 0;\n const l = isLE ? 0 : 4;\n view.setUint32(byteOffset + h, wh, isLE);\n view.setUint32(byteOffset + l, wl, isLE);\n}\n\n/** Choice: a ? b : c */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/** Majority function, true if any two inputs is true. */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n */\nexport abstract class HashMD<T extends HashMD<T>> extends Hash<T> {\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n super();\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: Input): this {\n aexists(this);\n data = toBytes(data);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path: we have at least one block in input, cast it to view and process\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\n setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n if (length % blockLen) to.buffer.set(buffer);\n return to;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */\nexport const SHA256_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state. Bits 32..64 of frac part of sqrt of primes 23..53 */\nexport const SHA224_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state. Bits 0..64 of frac part of sqrt of primes 23..53 */\nexport const SHA384_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state. Bits 0..64 of frac part of sqrt of primes 2..19 */\nexport const SHA512_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n", "/**\n * SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.\n * SHA256 is the fastest hash implementable in JS, even faster than Blake3.\n * Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and\n * [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\n * @module\n */\nimport { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\nimport { type CHash, clean, createHasher, rotr } from './utils.ts';\n\n/**\n * Round constants:\n * First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)\n */\n// prettier-ignore\nconst SHA256_K = /* @__PURE__ */ Uint32Array.from([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2\n]);\n\n/** Reusable temporary buffer. \"W\" comes straight from spec. */\nconst SHA256_W = /* @__PURE__ */ new Uint32Array(64);\nexport class SHA256 extends HashMD<SHA256> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n protected A: number = SHA256_IV[0] | 0;\n protected B: number = SHA256_IV[1] | 0;\n protected C: number = SHA256_IV[2] | 0;\n protected D: number = SHA256_IV[3] | 0;\n protected E: number = SHA256_IV[4] | 0;\n protected F: number = SHA256_IV[5] | 0;\n protected G: number = SHA256_IV[6] | 0;\n protected H: number = SHA256_IV[7] | 0;\n\n constructor(outputLen: number = 32) {\n super(64, outputLen, 8, false);\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { A, B, C, D, E, F, G, H } = this;\n return [A, B, C, D, E, F, G, H];\n }\n // prettier-ignore\n protected set(\n A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number\n ): void {\n this.A = A | 0;\n this.B = B | 0;\n this.C = C | 0;\n this.D = D | 0;\n this.E = E | 0;\n this.F = F | 0;\n this.G = G | 0;\n this.H = H | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);\n for (let i = 16; i < 64; i++) {\n const W15 = SHA256_W[i - 15];\n const W2 = SHA256_W[i - 2];\n const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);\n const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);\n SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;\n }\n // Compression function main loop, 64 rounds\n let { A, B, C, D, E, F, G, H } = this;\n for (let i = 0; i < 64; i++) {\n const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);\n const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);\n const T2 = (sigma0 + Maj(A, B, C)) | 0;\n H = G;\n G = F;\n F = E;\n E = (D + T1) | 0;\n D = C;\n C = B;\n B = A;\n A = (T1 + T2) | 0;\n }\n // Add the compressed chunk to the current hash value\n A = (A + this.A) | 0;\n B = (B + this.B) | 0;\n C = (C + this.C) | 0;\n D = (D + this.D) | 0;\n E = (E + this.E) | 0;\n F = (F + this.F) | 0;\n G = (G + this.G) | 0;\n H = (H + this.H) | 0;\n this.set(A, B, C, D, E, F, G, H);\n }\n protected roundClean(): void {\n clean(SHA256_W);\n }\n destroy(): void {\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n clean(this.buffer);\n }\n}\n\nexport class SHA224 extends SHA256 {\n protected A: number = SHA224_IV[0] | 0;\n protected B: number = SHA224_IV[1] | 0;\n protected C: number = SHA224_IV[2] | 0;\n protected D: number = SHA224_IV[3] | 0;\n protected E: number = SHA224_IV[4] | 0;\n protected F: number = SHA224_IV[5] | 0;\n protected G: number = SHA224_IV[6] | 0;\n protected H: number = SHA224_IV[7] | 0;\n constructor() {\n super(28);\n }\n}\n\n// SHA2-512 is slower than sha256 in js because u64 operations are slow.\n\n// Round contants\n// First 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409\n// prettier-ignore\nconst K512 = /* @__PURE__ */ (() => u64.split([\n '0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',\n '0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',\n '0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',\n '0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',\n '0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',\n '0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',\n '0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',\n '0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',\n '0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',\n '0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',\n '0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',\n '0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',\n '0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',\n '0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',\n '0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',\n '0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',\n '0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',\n '0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',\n '0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',\n '0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'\n].map(n => BigInt(n))))();\nconst SHA512_Kh = /* @__PURE__ */ (() => K512[0])();\nconst SHA512_Kl = /* @__PURE__ */ (() => K512[1])();\n\n// Reusable temporary buffers\nconst SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);\nconst SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);\n\nexport class SHA512 extends HashMD<SHA512> {\n // We cannot use array here since array allows indexing by variable\n // which means optimizer/compiler cannot use registers.\n // h -- high 32 bits, l -- low 32 bits\n protected Ah: number = SHA512_IV[0] | 0;\n protected Al: number = SHA512_IV[1] | 0;\n protected Bh: number = SHA512_IV[2] | 0;\n protected Bl: number = SHA512_IV[3] | 0;\n protected Ch: number = SHA512_IV[4] | 0;\n protected Cl: number = SHA512_IV[5] | 0;\n protected Dh: number = SHA512_IV[6] | 0;\n protected Dl: number = SHA512_IV[7] | 0;\n protected Eh: number = SHA512_IV[8] | 0;\n protected El: number = SHA512_IV[9] | 0;\n protected Fh: number = SHA512_IV[10] | 0;\n protected Fl: number = SHA512_IV[11] | 0;\n protected Gh: number = SHA512_IV[12] | 0;\n protected Gl: number = SHA512_IV[13] | 0;\n protected Hh: number = SHA512_IV[14] | 0;\n protected Hl: number = SHA512_IV[15] | 0;\n\n constructor(outputLen: number = 64) {\n super(128, outputLen, 16, false);\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];\n }\n // prettier-ignore\n protected set(\n Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,\n Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number\n ): void {\n this.Ah = Ah | 0;\n this.Al = Al | 0;\n this.Bh = Bh | 0;\n this.Bl = Bl | 0;\n this.Ch = Ch | 0;\n this.Cl = Cl | 0;\n this.Dh = Dh | 0;\n this.Dl = Dl | 0;\n this.Eh = Eh | 0;\n this.El = El | 0;\n this.Fh = Fh | 0;\n this.Fl = Fl | 0;\n this.Gh = Gh | 0;\n this.Gl = Gl | 0;\n this.Hh = Hh | 0;\n this.Hl = Hl | 0;\n }\n protected process(view: DataView, offset: number): void {\n // Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array\n for (let i = 0; i < 16; i++, offset += 4) {\n SHA512_W_H[i] = view.getUint32(offset);\n SHA512_W_L[i] = view.getUint32((offset += 4));\n }\n for (let i = 16; i < 80; i++) {\n // s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)\n const W15h = SHA512_W_H[i - 15] | 0;\n const W15l = SHA512_W_L[i - 15] | 0;\n const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);\n const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);\n // s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)\n const W2h = SHA512_W_H[i - 2] | 0;\n const W2l = SHA512_W_L[i - 2] | 0;\n const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);\n const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);\n // SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];\n const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);\n const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);\n SHA512_W_H[i] = SUMh | 0;\n SHA512_W_L[i] = SUMl | 0;\n }\n let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;\n // Compression function main loop, 80 rounds\n for (let i = 0; i < 80; i++) {\n // S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)\n const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);\n const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);\n //const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;\n const CHIh = (Eh & Fh) ^ (~Eh & Gh);\n const CHIl = (El & Fl) ^ (~El & Gl);\n // T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]\n // prettier-ignore\n const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);\n const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);\n const T1l = T1ll | 0;\n // S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)\n const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);\n const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);\n const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);\n const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);\n Hh = Gh | 0;\n Hl = Gl | 0;\n Gh = Fh | 0;\n Gl = Fl | 0;\n Fh = Eh | 0;\n Fl = El | 0;\n ({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));\n Dh = Ch | 0;\n Dl = Cl | 0;\n Ch = Bh | 0;\n Cl = Bl | 0;\n Bh = Ah | 0;\n Bl = Al | 0;\n const All = u64.add3L(T1l, sigma0l, MAJl);\n Ah = u64.add3H(All, T1h, sigma0h, MAJh);\n Al = All | 0;\n }\n // Add the compressed chunk to the current hash value\n ({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));\n ({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));\n ({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));\n ({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));\n ({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));\n ({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));\n ({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));\n ({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));\n this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);\n }\n protected roundClean(): void {\n clean(SHA512_W_H, SHA512_W_L);\n }\n destroy(): void {\n clean(this.buffer);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\nexport class SHA384 extends SHA512 {\n protected Ah: number = SHA384_IV[0] | 0;\n protected Al: number = SHA384_IV[1] | 0;\n protected Bh: number = SHA384_IV[2] | 0;\n protected Bl: number = SHA384_IV[3] | 0;\n protected Ch: number = SHA384_IV[4] | 0;\n protected Cl: number = SHA384_IV[5] | 0;\n protected Dh: number = SHA384_IV[6] | 0;\n protected Dl: number = SHA384_IV[7] | 0;\n protected Eh: number = SHA384_IV[8] | 0;\n protected El: number = SHA384_IV[9] | 0;\n protected Fh: number = SHA384_IV[10] | 0;\n protected Fl: number = SHA384_IV[11] | 0;\n protected Gh: number = SHA384_IV[12] | 0;\n protected Gl: number = SHA384_IV[13] | 0;\n protected Hh: number = SHA384_IV[14] | 0;\n protected Hl: number = SHA384_IV[15] | 0;\n\n constructor() {\n super(48);\n }\n}\n\n/**\n * Truncated SHA512/256 and SHA512/224.\n * SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as \"intermediary\" IV of SHA512/t.\n * Then t hashes string to produce result IV.\n * See `test/misc/sha2-gen-iv.js`.\n */\n\n/** SHA512/224 IV */\nconst T224_IV = /* @__PURE__ */ Uint32Array.from([\n 0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,\n 0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,\n]);\n\n/** SHA512/256 IV */\nconst T256_IV = /* @__PURE__ */ Uint32Array.from([\n 0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,\n 0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,\n]);\n\nexport class SHA512_224 extends SHA512 {\n protected Ah: number = T224_IV[0] | 0;\n protected Al: number = T224_IV[1] | 0;\n protected Bh: number = T224_IV[2] | 0;\n protected Bl: number = T224_IV[3] | 0;\n protected Ch: number = T224_IV[4] | 0;\n protected Cl: number = T224_IV[5] | 0;\n protected Dh: number = T224_IV[6] | 0;\n protected Dl: number = T224_IV[7] | 0;\n protected Eh: number = T224_IV[8] | 0;\n protected El: number = T224_IV[9] | 0;\n protected Fh: number = T224_IV[10] | 0;\n protected Fl: number = T224_IV[11] | 0;\n protected Gh: number = T224_IV[12] | 0;\n protected Gl: number = T224_IV[13] | 0;\n protected Hh: number = T224_IV[14] | 0;\n protected Hl: number = T224_IV[15] | 0;\n\n constructor() {\n super(28);\n }\n}\n\nexport class SHA512_256 extends SHA512 {\n protected Ah: number = T256_IV[0] | 0;\n protected Al: number = T256_IV[1] | 0;\n protected Bh: number = T256_IV[2] | 0;\n protected Bl: number = T256_IV[3] | 0;\n protected Ch: number = T256_IV[4] | 0;\n protected Cl: number = T256_IV[5] | 0;\n protected Dh: number = T256_IV[6] | 0;\n protected Dl: number = T256_IV[7] | 0;\n protected Eh: number = T256_IV[8] | 0;\n protected El: number = T256_IV[9] | 0;\n protected Fh: number = T256_IV[10] | 0;\n protected Fl: number = T256_IV[11] | 0;\n protected Gh: number = T256_IV[12] | 0;\n protected Gl: number = T256_IV[13] | 0;\n protected Hh: number = T256_IV[14] | 0;\n protected Hl: number = T256_IV[15] | 0;\n\n constructor() {\n super(32);\n }\n}\n\n/**\n * SHA2-256 hash function from RFC 4634.\n *\n * It is the fastest JS hash, even faster than Blake3.\n * To break sha256 using birthday attack, attackers need to try 2^128 hashes.\n * BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n */\nexport const sha256: CHash = /* @__PURE__ */ createHasher(() => new SHA256());\n/** SHA2-224 hash function from RFC 4634 */\nexport const sha224: CHash = /* @__PURE__ */ createHasher(() => new SHA224());\n\n/** SHA2-512 hash function from RFC 4634. */\nexport const sha512: CHash = /* @__PURE__ */ createHasher(() => new SHA512());\n/** SHA2-384 hash function from RFC 4634. */\nexport const sha384: CHash = /* @__PURE__ */ createHasher(() => new SHA384());\n\n/**\n * SHA2-512/256 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_256: CHash = /* @__PURE__ */ createHasher(() => new SHA512_256());\n/**\n * SHA2-512/224 \"truncated\" hash function, with improved resistance to length extension attacks.\n * See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).\n */\nexport const sha512_224: CHash = /* @__PURE__ */ createHasher(() => new SHA512_224());\n", "/**\n * SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.\n *\n * To break sha256 using birthday attack, attackers need to try 2^128 hashes.\n * BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.\n *\n * Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).\n * @module\n * @deprecated\n */\nimport {\n SHA224 as SHA224n,\n sha224 as sha224n,\n SHA256 as SHA256n,\n sha256 as sha256n,\n} from './sha2.ts';\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA256: typeof SHA256n = SHA256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha256: typeof sha256n = sha256n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const SHA224: typeof SHA224n = SHA224n;\n/** @deprecated Use import from `noble/hashes/sha2` module */\nexport const sha224: typeof sha224n = sha224n;\n", "import { sha256 } from '@noble/hashes/sha256'\n\n/** Fixed-width bytes before a record's digest list. */\nexport const BCPV2_HEADER_LENGTH = 68\nexport const BCPV2_SIGNATURE_LENGTH = 64\nexport const BCPV2_MIN_RECORD_LENGTH = BCPV2_HEADER_LENGTH + 32 + BCPV2_SIGNATURE_LENGTH\nexport const BCPV2_MAX_RECORD_LENGTH = 1316\nexport const BCPV2_MAX_DIGESTS = 37\nexport const BCPV2_HASH_ALG_SHA256 = 1\n\nexport interface BcProvenanceV2Endpoint {\n /** S in the AMBI pseudoheader and signature context. */\n sourceAddress: string\n /** G in the AMBI pseudoheader and signature context. */\n groupAddress: string\n /** UDP source and destination port of protected datagrams and, in band, of their records. */\n port: number\n /** Catalog-declared channel generation. */\n streamId: number\n}\n\nexport interface BcProvenanceV2Record {\n kid: number\n epoch: number\n mseq: bigint\n firstSeq: bigint\n count: number\n hashAlg: number\n flags: number\n sent: number\n ctx: Uint8Array\n digests: Uint8Array[]\n signature: Uint8Array\n /** Record bytes covered by the signature, excluding the signature itself. */\n body: Uint8Array\n /** A defensive copy of the complete wire record. */\n raw: Uint8Array\n}\n\nconst text = new TextEncoder()\nconst MAGIC = Uint8Array.from([0x4d, 0x48, 0x4d, 0x32]) // MHM2\n\n/**\n * multicast-02 7.2.7: a datagram on a protected flow's `port` whose first four\n * octets are \"MHM2\" is a record candidate (step 1); every other one is data\n * (step 2). Nothing else is read to decide.\n */\nexport function isBcpV2RecordCandidate(datagram: Uint8Array): boolean {\n return datagram.byteLength >= 4 &&\n datagram[0] === MAGIC[0] && datagram[1] === MAGIC[1] && datagram[2] === MAGIC[2] && datagram[3] === MAGIC[3]\n}\nconst SIGNING_DOMAIN = text.encode('mcast-manifest-v2\\0')\n\nfunction equalBytes(left: Uint8Array, right: Uint8Array): boolean {\n if (left.byteLength !== right.byteLength) return false\n let different = 0\n for (let index = 0; index < left.byteLength; index++) different |= left[index]! ^ right[index]!\n return different === 0\n}\n\nfunction u16(value: number): Uint8Array {\n const result = new Uint8Array(2)\n new DataView(result.buffer).setUint16(0, value)\n return result\n}\n\nfunction u32(value: number): Uint8Array {\n const result = new Uint8Array(4)\n new DataView(result.buffer).setUint32(0, value)\n return result\n}\n\nfunction validU32(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffff_ffff\n}\n\nfunction validPort(value: number): boolean {\n return Number.isInteger(value) && value >= 0 && value <= 0xffff\n}\n\nfunction parseIpv4(address: string): Uint8Array {\n const fields = address.split('.')\n if (fields.length !== 4 || fields.some((field) => !/^\\d+$/.test(field))) {\n throw new Error(`invalid IPv4 address ${address}`)\n }\n const values = fields.map(Number)\n if (values.some((value, index) => value > 255 || String(value) !== fields[index])) {\n throw new Error(`invalid IPv4 address ${address}`)\n }\n return Uint8Array.from(values)\n}\n\nfunction parseIpv6(address: string): Uint8Array {\n const compressed = address.includes('::')\n if ((address.match(/::/g) ?? []).length > 1) throw new Error(`invalid IPv6 address ${address}`)\n const [leftText, rightText = ''] = address.split('::')\n const left = leftText ? leftText.split(':') : []\n const right = rightText ? rightText.split(':') : []\n // RFC 3986 IPv6address: an ls32 dotted tail may end the literal (multicast-02\n // 7.2.6), never precede \"::\"; it stands for the last two h16 groups.\n const tail = compressed ? right : left\n if (tail.length > 0 && tail[tail.length - 1]!.includes('.')) {\n const v4 = parseIpv4(tail.pop()!)\n tail.push(((v4[0]! << 8) | v4[1]!).toString(16), ((v4[2]! << 8) | v4[3]!).toString(16))\n }\n if (left.some((group) => group.includes('.')) || right.some((group) => group.includes('.'))) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const missing = 8 - left.length - right.length\n if (missing < 0 || (!compressed && missing !== 0) || (compressed && missing < 1)) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const groups = [...left, ...Array.from({ length: missing }, () => '0'), ...right]\n if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/i.test(group))) {\n throw new Error(`invalid IPv6 address ${address}`)\n }\n const result = new Uint8Array(16)\n const view = new DataView(result.buffer)\n groups.forEach((group, index) => view.setUint16(index * 2, Number.parseInt(group, 16)))\n return result\n}\n\n/** Parse an IPv4 or IPv6 address in the native-width form used on the wire. */\nexport function parseBcProvenanceV2Address(address: string): Uint8Array {\n if (address.includes(':')) return parseIpv6(address)\n return parseIpv4(address)\n}\n\nfunction endpointBytes(endpoint: BcProvenanceV2Endpoint): {\n source: Uint8Array\n group: Uint8Array\n} {\n if (!validPort(endpoint.port) || !validU32(endpoint.streamId)) {\n throw new Error('invalid BCPV2 endpoint')\n }\n const source = parseBcProvenanceV2Address(endpoint.sourceAddress)\n const group = parseBcProvenanceV2Address(endpoint.groupAddress)\n if (source.byteLength !== group.byteLength) throw new Error('endpoint address families differ')\n return { source, group }\n}\n\nfunction readU32(view: DataView, offset: number): number {\n return view.getUint32(offset)\n}\n\nfunction readU64(view: DataView, offset: number): bigint {\n return view.getBigUint64(offset)\n}\n\n/**\n * Decode the fixed-layout BCPV2 record from multicast-02 Section 7.2.3.\n * Structural validation happens before callers inspect the variable digest list.\n */\nexport function decodeBcProvenanceV2Record(input: Uint8Array): BcProvenanceV2Record {\n const bytes = Uint8Array.from(input)\n if (\n bytes.byteLength < BCPV2_MIN_RECORD_LENGTH ||\n bytes.byteLength > BCPV2_MAX_RECORD_LENGTH ||\n (bytes.byteLength - 132) % 32 !== 0\n ) {\n throw new Error('invalid BCPV2 record length')\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n if (!equalBytes(bytes.subarray(0, 4), MAGIC)) throw new Error('invalid BCPV2 record magic')\n const count = view.getUint16(28)\n if (count < 1 || count > BCPV2_MAX_DIGESTS || bytes.byteLength !== 132 + count * 32) {\n throw new Error('invalid BCPV2 record count')\n }\n const body = bytes.slice(0, bytes.byteLength - BCPV2_SIGNATURE_LENGTH)\n const digests: Uint8Array[] = []\n for (let index = 0; index < count; index++) {\n digests.push(bytes.slice(BCPV2_HEADER_LENGTH + index * 32, BCPV2_HEADER_LENGTH + (index + 1) * 32))\n }\n return {\n kid: readU32(view, 4),\n epoch: readU32(view, 8),\n mseq: readU64(view, 12),\n firstSeq: readU64(view, 20),\n count,\n hashAlg: bytes[30]!,\n flags: bytes[31]!,\n sent: readU32(view, 32),\n ctx: bytes.slice(36, 68),\n digests,\n signature: bytes.slice(bytes.byteLength - BCPV2_SIGNATURE_LENGTH),\n body,\n raw: bytes,\n }\n}\n\n/** Return the exact Ed25519 preimage from multicast-02 Section 7.2.4: the prefix binds `u16(port)`. */\nexport function bcProvenanceV2SignedMessage(\n record: BcProvenanceV2Record,\n endpoint: BcProvenanceV2Endpoint,\n): Uint8Array {\n const { source, group } = endpointBytes(endpoint)\n const prefix = new Uint8Array(SIGNING_DOMAIN.byteLength + 4 + source.byteLength + group.byteLength + 2)\n let offset = 0\n prefix.set(SIGNING_DOMAIN, offset)\n offset += SIGNING_DOMAIN.byteLength\n prefix.set(u32(endpoint.streamId), offset)\n offset += 4\n prefix.set(source, offset)\n offset += source.byteLength\n prefix.set(group, offset)\n offset += group.byteLength\n prefix.set(u16(endpoint.port), offset)\n const message = new Uint8Array(prefix.byteLength + record.body.byteLength)\n message.set(prefix)\n message.set(record.body, prefix.byteLength)\n return message\n}\n\n/**\n * Compute the AMBI Section 3.4.2 digest for one transmitted datagram.\n * The payload includes the Source FEC Payload ID when one was transmitted.\n */\nexport function bcProvenanceV2DatagramDigest(\n endpoint: BcProvenanceV2Endpoint,\n payload: Uint8Array,\n): Uint8Array {\n if (payload.byteLength > 0xffff) throw new Error('datagram payload exceeds u16 length')\n const { source, group } = endpointBytes(endpoint)\n const pseudoheader = new Uint8Array(source.byteLength + group.byteLength + 2 + 2 + 2 + 2 + 4)\n let offset = 0\n pseudoheader.set(source, offset)\n offset += source.byteLength\n pseudoheader.set(group, offset)\n offset += group.byteLength\n pseudoheader.set([0, 0x11], offset)\n offset += 2\n pseudoheader.set(u16(payload.byteLength), offset)\n offset += 2\n pseudoheader.set(u16(endpoint.port), offset)\n offset += 2\n pseudoheader.set(u16(endpoint.port), offset)\n offset += 2\n pseudoheader.set(u32(endpoint.streamId), offset)\n const input = new Uint8Array(pseudoheader.byteLength + payload.byteLength)\n input.set(pseudoheader)\n input.set(payload, pseudoheader.byteLength)\n return sha256(input)\n}\n\n/** Verify the ordered digest list against the transmitted datagrams. */\nexport function verifyBcProvenanceV2Datagrams(\n record: BcProvenanceV2Record,\n endpoint: BcProvenanceV2Endpoint,\n datagrams: readonly Uint8Array[],\n): boolean {\n return record.hashAlg === BCPV2_HASH_ALG_SHA256 &&\n datagrams.length === record.count &&\n datagrams.every((datagram, index) => equalBytes(\n bcProvenanceV2DatagramDigest(endpoint, datagram),\n record.digests[index]!,\n ))\n}\n\nexport interface BcProvenanceV2Params {\n codec: string\n /** 0 where MOQ-MMT Section 4.4.2 resolves none. */\n timescale: number\n groupDurationTicks: number\n /** The entry's `fec` array (fec-catalog-00 3.1); absent or empty for no FEC. */\n fec?: readonly BcProvenanceV2FecInstance[]\n}\n\nexport interface BcProvenanceV2FecInstance {\n /** Orders the tuples, never hashed; 0 when absent. */\n instanceId?: number\n /** Registered scheme identifier; \"none\" is reserved and rejected. */\n scheme: string\n sourceSymbols: number\n repairSymbols: number\n symbolSize: number\n interleaveDepthMs?: number\n}\n\nfunction concat(parts: Uint8Array[]): Uint8Array {\n const out = new Uint8Array(parts.reduce((n, p) => n + p.byteLength, 0))\n let offset = 0\n for (const part of parts) {\n out.set(part, offset)\n offset += part.byteLength\n }\n return out\n}\n\nfunction lp16(value: Uint8Array | string): Uint8Array {\n const bytes = typeof value === 'string' ? text.encode(value) : value\n if (bytes.byteLength > 0xffff) throw new Error('lp16 field exceeds u16 length')\n return concat([u16(bytes.byteLength), bytes])\n}\n\n/**\n * `params_hash` of multicast-02 Section 7.2.4, hash_alg 1: one tuple per FEC\n * instance in ascending `instanceId`, neither the count nor an `instanceId`\n * encoded; no instance hashes as `lp16(\"none\")` then four u32(0).\n */\nexport function bcProvenanceV2ParamsHash(params: BcProvenanceV2Params): Uint8Array {\n const fec = [...(params.fec ?? [])].sort((a, b) => (a.instanceId ?? 0) - (b.instanceId ?? 0))\n if (fec.some((instance) => instance.scheme === 'none')) {\n throw new Error('FEC scheme \"none\" is reserved (fec-core-00 15.2)')\n }\n const tuples = fec.length === 0\n ? [lp16('none'), u32(0), u32(0), u32(0), u32(0)]\n : fec.flatMap((instance) => [\n lp16(instance.scheme),\n u32(instance.sourceSymbols),\n u32(instance.repairSymbols),\n u32(instance.symbolSize),\n u32(instance.interleaveDepthMs ?? 0),\n ])\n return sha256(concat([\n text.encode('BCPV2-params'),\n lp16(params.codec),\n u32(params.timescale),\n u32(params.groupDurationTicks),\n ...tuples,\n ]))\n}\n\n/**\n * `ctx` of multicast-02 Section 7.2.4: the namespace fields in order, the endpoint's\n * packetIds ascending, and `params_hash`. A flow without a MoQ catalog uses 32 zero octets.\n */\nexport function bcProvenanceV2Ctx(\n namespace: readonly (Uint8Array | string)[],\n packetIds: readonly number[],\n params: BcProvenanceV2Params,\n): Uint8Array {\n const ids = [...packetIds].sort((a, b) => a - b)\n if (namespace.length > 0xffff || ids.length > 0xffff) throw new Error('ctx field count exceeds u16')\n return sha256(concat([\n text.encode('BCPV2-ctx'),\n u16(namespace.length),\n ...namespace.map(lp16),\n u16(ids.length),\n ...ids.map(u16),\n bcProvenanceV2ParamsHash(params),\n ]))\n}\n", "/**\n * `multicast.auth` and an endpoint's `auth`: bc-provenance version 2, per\n * draft-ramadan-moq-multicast-02 7.2.6 at Blockcast/moqcast-draft@5f49172.\n * Records ride in band on the flow's own `port` (7.2.3); draft -01's separate\n * record port is gone.\n *\n * The block lives in the `multicast` object, never the root: the root dotted\n * key `\"multicast.auth\"` was draft -00's (libmmt called its `tracks[]` shape\n * \"multicast.auth v2 (BLO-16700)\", which is the draft's bc-provenance\n * version 1) and is refused. `multicast.auth` declares the profile and its\n * keys; an endpoint's `auth` marks that endpoint's flow protected and carries\n * the flow's parameters. There is no verify-and-report mode (7.2.7): a\n * receiver that implements the profile enforces it.\n *\n * The rules are `multicastCatalogIssues` in `@blockcast/mmt-base`, shared with\n * the transport receivers; this module decodes what they admit.\n */\n\nimport {\n\tBCP_DEFAULT_MANIFEST_TIMER_MS,\n\tBCP_DEFAULT_MAX_AUTH_DELAY_MS,\n\tcanonicalIpAddress,\n\tendpointAuthTiming,\n\tmulticastCatalogIssues,\n} from \"@blockcast/mmt-base\";\nimport { bcProvenanceV2Ctx, type BcProvenanceV2Params, type BcpV2Key } from \"@blockcast/mmt-manifest-verify\";\n\nexport type MulticastAuthScheme = \"bc-provenance\";\nexport type MulticastAuthSig = \"ed25519\";\n\nconst RAW_ED25519_KEY_LENGTH = 32;\n\n/** Default `maxAuthDelayMs` and `manifestTimerMs` of an endpoint's `auth`. */\nexport const DEFAULT_MAX_AUTH_DELAY_MS = BCP_DEFAULT_MAX_AUTH_DELAY_MS;\nexport const DEFAULT_MANIFEST_TIMER_MS = BCP_DEFAULT_MANIFEST_TIMER_MS;\n\nconst BASE64URL_ALPHABET =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n/** Encode raw bytes as unpadded base64url \u2014 the one accepted encoding for\n * `multicast.auth.keys[].publicKey`. */\nexport function encodeBase64Url(bytes: Uint8Array): string {\n\tlet output = \"\";\n\tfor (let i = 0; i < bytes.length; i += 3) {\n\t\tconst b0 = bytes[i]!;\n\t\tconst b1 = bytes[i + 1];\n\t\tconst b2 = bytes[i + 2];\n\t\toutput += BASE64URL_ALPHABET[b0 >> 2];\n\t\toutput += BASE64URL_ALPHABET[((b0 & 0x03) << 4) | (b1 === undefined ? 0 : b1 >> 4)];\n\t\tif (b1 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[((b1 & 0x0f) << 2) | (b2 === undefined ? 0 : b2 >> 6)];\n\t\tif (b2 === undefined) break;\n\t\toutput += BASE64URL_ALPHABET[b2 & 0x3f];\n\t}\n\treturn output;\n}\n\nconst BASE64URL_DECODE_TABLE = (() => {\n\tconst table = new Int8Array(128).fill(-1);\n\tfor (let i = 0; i < BASE64URL_ALPHABET.length; i++) {\n\t\ttable[BASE64URL_ALPHABET.charCodeAt(i)] = i;\n\t}\n\treturn table;\n})();\n\n/** Decode unpadded base64url text to raw bytes. Throws on any padding\n * (`=`), standard-alphabet (`+`/`/`), or otherwise invalid character. */\nexport function decodeBase64Url(text: string): Uint8Array {\n\tif (text.length % 4 === 1) {\n\t\tthrow new Error(`[multicast-auth-catalog] invalid unpadded base64url length for \"${text}\"`);\n\t}\n\tconst bytes: number[] = [];\n\tlet buffer = 0;\n\tlet bits = 0;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst code = text.charCodeAt(i);\n\t\tconst value = code < 128 ? BASE64URL_DECODE_TABLE[code] : -1;\n\t\tif (value === undefined || value < 0) {\n\t\t\tthrow new Error(`[multicast-auth-catalog] invalid base64url character in \"${text}\"`);\n\t\t}\n\t\tbuffer = (buffer << 6) | value;\n\t\tbits += 6;\n\t\tif (bits >= 8) {\n\t\t\tbits -= 8;\n\t\t\tbytes.push((buffer >> bits) & 0xff);\n\t\t}\n\t}\n\tconst decoded = Uint8Array.from(bytes);\n\tif (encodeBase64Url(decoded) !== text) {\n\t\tthrow new Error(`[multicast-auth-catalog] non-canonical base64url encoding \"${text}\"`);\n\t}\n\treturn decoded;\n}\n\n/** Decode a catalog key in the only accepted representation: exactly 32 raw\n * ed25519 bytes encoded as canonical, unpadded base64url. */\nexport function decodeEd25519PublicKey(publicKey: string): Uint8Array {\n\tconst bytes = decodeBase64Url(publicKey);\n\tif (bytes.byteLength !== RAW_ED25519_KEY_LENGTH) {\n\t\tthrow new Error(\n\t\t\t`[multicast-auth-catalog] public key must decode to ${RAW_ED25519_KEY_LENGTH} raw bytes, got ${bytes.byteLength}`,\n\t\t);\n\t}\n\treturn bytes;\n}\n\n/** One `multicast.auth.keys[]` entry: `kid` and `firstEpoch` are u32,\n * `publicKey` the 32-octet Ed25519 key in canonical unpadded base64url. */\nexport interface MulticastAuthKey {\n\tkid: number;\n\tpublicKey: string;\n\tfirstEpoch: number;\n}\n\n/** `multicast.auth` (7.2.6). `hash_alg` is a record octet, not a member. */\nexport interface MulticastAuthConfig {\n\tscheme: MulticastAuthScheme;\n\tversion: 2;\n\tsig: MulticastAuthSig;\n\t/** One to four keys; `kid` and `firstEpoch` each distinct. */\n\tkeys: MulticastAuthKey[];\n\t/** Lowest `epoch` a receiver accepts at join. */\n\tminEpoch: number;\n}\n\n/** An endpoint's `auth` (7.2.6). A protected endpoint also declares `bandwidth`. */\nexport interface EndpointAuthConfig {\n\t/** u32 chosen per channel generation. */\n\tstreamId: number;\n\t/** `S` of a protected ASM endpoint; never on SSM. */\n\tsenderAddress?: string;\n\t/** 50..1000, default 200; a receiver clamps one outside the range. */\n\tmaxAuthDelayMs?: number;\n\t/** At least 1, default 20; a receiver ignores the endpoint below 1. */\n\tmanifestTimerMs?: number;\n\t/** MoQ track mirroring the records. */\n\tmanifestTrack?: string;\n}\n\n/** Structural check of a `multicast.auth` block, duplicate `kid` and\n * `firstEpoch` included. */\nexport function isMulticastAuthConfig(obj: unknown): obj is MulticastAuthConfig {\n\treturn multicastCatalogIssues({ multicast: { auth: obj } }).length === 0;\n}\n\n/** A protected flow as a receiver binds it: one per protected endpoint. */\nexport interface ResolvedMulticastAuthFlow {\n\t/** Index of the endpoint in `multicast.endpoints`. */\n\tendpointIndex: number;\n\t/** `S`: `sourceAddress` on SSM, `auth.senderAddress` on ASM; canonical (`canonicalIpAddress`). */\n\tsource: string;\n\t/** Canonical (`canonicalIpAddress`). */\n\tgroupAddress: string;\n\t/** The flow's port; its records ride in band on it (7.2.3). */\n\tport: number;\n\tbandwidth: number;\n\tstreamId: number;\n\tmaxAuthDelayMs: number;\n\tmanifestTimerMs: number;\n\tmanifestTrack?: string;\n\ttracks: Array<{ name: string; packetId: number }>;\n\t/** `ctx` recomputed from the catalog (7.2.4); present when `namespaceFields` was given. */\n\tctx?: Uint8Array;\n}\n\nexport interface ResolvedMulticastAuthConfig {\n\t/** Decoded keys, ascending by `firstEpoch`. */\n\tkeys: BcpV2Key[];\n\tminEpoch: number;\n\tflows: ResolvedMulticastAuthFlow[];\n}\n\n/**\n * Resolve `multicast.auth` and every protected endpoint into decoded keys and\n * per-endpoint flows. Returns `undefined` for an unprotected catalog unless\n * `requireAuth` pins authentication (7.2.7), which then throws.\n *\n * A catalog-level fault (no key set: a malformed block, a repeated `kid` or\n * `firstEpoch`, the retired root key) throws, so nothing is admitted and\n * nothing falls back to unauthenticated reception. An endpoint the rules make\n * a receiver ignore yields no flow; the others still bind.\n *\n * `namespaceFields` are the Track Namespace Fields of the catalog track, as\n * MOQT carries them. With them every flow carries the recomputed `ctx`, which\n * the receiver compares against each record; without them `ctx` is carried\n * blind (7.2.4).\n */\nexport function resolveMulticastAuthCatalog(\n\tcatalog: unknown,\n\toptions: {\n\t\trequireAuth?: boolean;\n\t\tcatalogNamespace?: string;\n\t\tnamespaceFields?: readonly (string | Uint8Array)[];\n\t} = {},\n): ResolvedMulticastAuthConfig | undefined {\n\tif (typeof catalog !== \"object\" || catalog === null || Array.isArray(catalog)) {\n\t\tthrow new Error(\"[multicast-auth-catalog] catalog must be an object\");\n\t}\n\tconst issues = multicastCatalogIssues(catalog, options);\n\tconst fatal = issues.find((issue) => issue.endpointIndex === undefined);\n\tif (fatal) throw new Error(`[multicast-auth-catalog] ${fatal.path}: ${fatal.message}`);\n\tconst multicast = (catalog as Record<string, unknown>)[\"multicast\"] as Record<string, unknown> | undefined;\n\tconst auth = multicast?.[\"auth\"] as MulticastAuthConfig | undefined;\n\tif (auth === undefined) {\n\t\tif (options.requireAuth) {\n\t\t\tthrow new Error(\"[multicast-auth-catalog] authentication is pinned but the catalog declares no multicast.auth\");\n\t\t}\n\t\treturn undefined;\n\t}\n\tconst ignored = new Set(issues.map((issue) => issue.endpointIndex));\n\tconst endpoints = (multicast![\"endpoints\"] ?? []) as Array<Record<string, unknown>>;\n\tconst catalogTracks = ((catalog as Record<string, unknown>)[\"tracks\"] ?? []) as Array<Record<string, unknown>>;\n\tconst flows: ResolvedMulticastAuthFlow[] = [];\n\tendpoints.forEach((endpoint, endpointIndex) => {\n\t\tif (ignored.has(endpointIndex)) return;\n\t\tconst flowAuth = endpoint[\"auth\"] as EndpointAuthConfig;\n\t\tconst tracks = ((endpoint[\"tracks\"] ?? []) as Array<{ name: string; packetId: number }>).map(({ name, packetId }) => ({\n\t\t\tname,\n\t\t\tpacketId,\n\t\t}));\n\t\tlet ctx: Uint8Array | undefined;\n\t\tif (options.namespaceFields !== undefined) {\n\t\t\tconst params = ctxParams(catalogTracks, tracks);\n\t\t\t// No catalog entry to hash: the endpoint is ignored, nothing is read unauthenticated.\n\t\t\tif (params === undefined) return;\n\t\t\tctx = bcProvenanceV2Ctx(options.namespaceFields, tracks.map((track) => track.packetId), params);\n\t\t}\n\t\tflows.push({\n\t\t\tendpointIndex,\n\t\t\tsource: canonicalIpAddress(endpoint[\"sourceAddress\"] ?? flowAuth.senderAddress)!,\n\t\t\tgroupAddress: canonicalIpAddress(endpoint[\"groupAddress\"])!,\n\t\t\tport: endpoint[\"port\"] as number,\n\t\t\tbandwidth: endpoint[\"bandwidth\"] as number,\n\t\t\tstreamId: flowAuth.streamId,\n\t\t\t// Clamped, or the defaults where absent (7.2.6).\n\t\t\t...endpointAuthTiming(flowAuth),\n\t\t\ttracks,\n\t\t\t...(ctx === undefined ? {} : { ctx }),\n\t\t});\n\t});\n\treturn {\n\t\tkeys: auth.keys\n\t\t\t.map((key) => ({ kid: key.kid, publicKey: decodeEd25519PublicKey(key.publicKey), firstEpoch: key.firstEpoch }))\n\t\t\t.sort((a, b) => a.firstEpoch - b.firstEpoch),\n\t\tminEpoch: auth.minEpoch,\n\t\tflows,\n\t};\n}\n\n/** A positive integer that fits the u32 it is hashed as (7.2.4), else `0`. */\nfunction positiveInteger(value: unknown): number {\n\treturn typeof value === \"number\" && Number.isSafeInteger(value) && value > 0 && value <= 0xffffffff ? value : 0;\n}\n\n/**\n * `params_hash` inputs (multicast-02 7.2.4): the catalog entry of the\n * endpoint's lowest-`packetId` media track or, for a repair-only endpoint, of\n * the source its lowest-`packetId` repair track repairs (its `source`, else\n * the track one of whose `fec` instances names it as `repairTrack`). A\n * timescale or group duration that does not resolve to an integer tick count\n * is `0`. An instance naming the reserved scheme \"none\" (fec-core-00 15.2)\n * has no params_hash, so its endpoint is ignored like one with no entry.\n */\nfunction ctxParams(\n\tcatalogTracks: Array<Record<string, unknown>>,\n\tlisted: Array<{ name: string; packetId: number }>,\n): BcProvenanceV2Params | undefined {\n\tconst byName = new Map(catalogTracks.map((track) => [track[\"name\"], track]));\n\tconst instancesOf = (track: Record<string, unknown>): Array<Record<string, unknown>> =>\n\t\tArray.isArray(track[\"fec\"]) ? (track[\"fec\"] as unknown[]).filter((f): f is Record<string, unknown> => typeof f === \"object\" && f !== null) : [];\n\tconst ordered = [...listed].sort((a, b) => a.packetId - b.packetId).map(({ name }) => byName.get(name));\n\tlet entry = ordered.find((track) => track !== undefined && track[\"packaging\"] !== \"fec-repair\");\n\tif (entry === undefined) {\n\t\tconst repair = ordered.find((track) => track !== undefined);\n\t\tif (repair === undefined) return undefined;\n\t\tentry = typeof repair[\"source\"] === \"string\"\n\t\t\t? byName.get(repair[\"source\"])\n\t\t\t: catalogTracks.find((track) => instancesOf(track).some((f) => f[\"repairTrack\"] === repair[\"name\"]));\n\t\tif (entry === undefined) return undefined;\n\t}\n\tconst instances = instancesOf(entry);\n\tif (instances.some((f) => f[\"scheme\"] === \"none\")) return undefined;\n\tconst timescale = positiveInteger(entry[\"timescale\"]);\n\tconst ms = positiveInteger(entry[\"groupDurationMs\"]);\n\tconst groupDurationTicks = positiveInteger(entry[\"groupDurationTicks\"]) ||\n\t\tpositiveInteger(timescale && (ms * timescale) % 1000 === 0 ? (ms * timescale) / 1000 : 0);\n\treturn {\n\t\tcodec: typeof entry[\"codec\"] === \"string\" ? entry[\"codec\"] : \"\",\n\t\ttimescale,\n\t\tgroupDurationTicks,\n\t\tfec: instances.map((f) => ({\n\t\t\tinstanceId: typeof f[\"instanceId\"] === \"number\" ? f[\"instanceId\"] : 0,\n\t\t\tscheme: typeof f[\"scheme\"] === \"string\" ? f[\"scheme\"] : \"\",\n\t\t\tsourceSymbols: positiveInteger(f[\"sourceSymbols\"]),\n\t\t\trepairSymbols: positiveInteger(f[\"repairSymbols\"]),\n\t\t\tsymbolSize: positiveInteger(f[\"symbolSize\"]),\n\t\t\tinterleaveDepthMs: positiveInteger(f[\"interleaveDepthMs\"]),\n\t\t})),\n\t};\n}\n\n/** The key that signs a record of `kid` at `epoch`, or `undefined` when that\n * `kid` does not sign it (7.2.7: such a record is dropped before verification). */\nexport function multicastAuthKeyFor(\n\tresolved: ResolvedMulticastAuthConfig,\n\tkid: number,\n\tepoch: number,\n): Uint8Array | undefined {\n\t// `keys` is ascending by firstEpoch with unique kid and firstEpoch (resolve throws otherwise).\n\tconst signers = resolved.keys.filter((key) => key.firstEpoch <= epoch);\n\tconst signer = signers[signers.length - 1];\n\treturn signer?.kid === kid ? signer.publicKey : undefined;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["text", "text", "text", "sha256", "sha256", "text"]
|
|
7
7
|
}
|