Skip to content

Conversation

@1egoman
Copy link
Contributor

@1egoman 1egoman commented Jan 22, 2026

Implements binary serialization and deserialization for data track packets. For now, this is being introduced in isolation to reduce the review burden, and further data tracks work will be done in follow up pull requests which build on top of this.

Included in this change also is some relatively early stage error handling infrastructure to attempt to further iterate on the typesafe error handling pull requests that lukas has been working on. I think we both still aren't really satisfied with the current state of things (namely, TypedPromise doesn't really support synchronous results, and introducing a new separate synchronousResult type of some sort makes interop between these two types a challenge). A LLM suggested we explore this Throws branded type approach (see ./throws-transformer/README.md for more info about how it works) which seems to have some fairly early promise, so for now I've opted to build the data tracks error handling logic on top. That is part of what is resulting in the diff size of this change being so large 😬 .

Summary by CodeRabbit

  • New Features

    • Full data-track packet system: header/packet (de)serialization, extensions (E2EE, user timestamp), timestamps, and JSON output.
  • Tools

    • Throws-check tooling: CLI, language-service plugin, transformer, README, and CI validation step added.
  • Errors & Utilities

    • Typed error framework, serialization errors, wrap-around integers, timestamp utilities, and serialization base class.
  • Tests

    • Expanded unit tests covering packets, extensions, handles, and wrap-around behavior.
  • Chores

    • New package script and added tooling dependency; tsconfig plugin registration updated.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

🦋 Changeset detected

Latest commit: 95d52f4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
livekit-client Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds a Throws-branded type system and throws-transformer toolchain (engine/cli/plugin/transformer/README) integrated into tsconfig and CI; implements a DataTrack packet (de)serialization stack (utils, handle/allocator, constants, extensions, errors, serializable, packet) with extensive tests and public re-exports.

Changes

Cohort / File(s) Change Summary
CI & Packaging
/.github/workflows/test.yaml, package.json, tsconfig.json
Adds CI step "Throws check" (runs pnpm throws:check), adds throws:check script and glob dependency, registers throws-transformer plugin in tsconfig.json, and adds ES2022.Error to libs.
Public Re-exports
src/index.ts
Re-exports DataTrack packet and extension types into the public API.
Throws Types & Helpers
src/utils/throws.ts
Introduces branded Throws<T,E> and helpers (ExtractErrors, ExtractSuccess, CombineErrors, PropagatesErrors).
Throws Tooling: Engine & CLI
throws-transformer/engine.ts, throws-transformer/cli.ts
Adds static-analysis engine checkSourceFile and CLI runner that prints diagnostics and exits nonzero on failures.
Throws Tooling: Plugin & Transformer
throws-transformer/plugin.ts, throws-transformer/transformer.ts, throws-transformer/README.md
Adds Language Service plugin (diagnostic code 90001) and transformer wrapper emitting Throws<> diagnostics; includes README and CLI integration.
TypedPromise helper
src/utils/TypedPromise.ts
Adds overloaded static resolve to TypedPromise to preserve typed resolution.
Error Base Refactor
src/room/errors.ts
Introduces LivekitReasonedError, adds optional cause to LivekitError, and updates ConnectionError / DataStreamError / SignalRequestError to carry typed reason and reasonName.
DataTrack Utilities & Tests
src/room/data-track/utils.ts, src/room/data-track/utils.test.ts
Adds U16_MAX_SIZE, WrapAroundUnsignedInt (u16), DataTrackTimestamp, coerceToDataView, and unit tests for wrap-around behavior.
DataTrack Handle & Tests
src/room/data-track/handle.ts, src/room/data-track/handle.test.ts
Adds DataTrackHandle, DataTrackHandleError (Reserved/TooLarge), DataTrackHandleAllocator, validation and allocator behavior, plus tests for reserved/too-large cases.
Packet Serialization Foundation
src/room/data-track/packet/constants.ts, src/room/data-track/packet/serializable.ts
Adds binary layout constants and Serializable base with length/write contract and runtime write-length validation.
Packet Core + Tests
src/room/data-track/packet/index.ts, src/room/data-track/packet/index.test.ts
Implements DataTrackPacketHeader, DataTrackPacket, FrameMarker, full (de)serialization, versioning/extension handling, JSON outputs, and extensive tests including edge cases and error paths.
Packet Extensions
src/room/data-track/packet/extensions.ts
Adds DataTrackExtensionTag, abstract DataTrackExtension, DataTrackUserTimestampExtension, DataTrackE2eeExtension, and DataTrackExtensions container with robust fromBinary parsing, padding, and forward-compat handling.
Packet Errors
src/room/data-track/packet/errors.ts
Adds DataTrackDeserializeErrorReason / DataTrackDeserializeError and DataTrackSerializeErrorReason / DataTrackSerializeError with factory constructors and typed causes.
Throws Transformer Tests & Tooling Docs
throws-transformer/*
Adds README and wiring for developer tooling; includes transformer that emits diagnostics and a CLI for CI checks.
Release Note
.changeset/busy-ends-shake.md
Adds a patch release note describing new DataTrack packet serialization and throws-transformer additions.

Sequence Diagram(s)

sequenceDiagram
    participant Editor as Developer Editor
    participant LS as TS Language Service
    participant Plugin as throws-transformer Plugin
    participant Engine as throws-transformer Engine
    participant CLI as throws-transformer CLI
    participant CI as CI Runner

    Editor->>LS: request diagnostics for file
    LS->>Plugin: getSemanticDiagnostics(sourceFile)
    Plugin->>Engine: checkSourceFile(sourceFile, checker)
    Engine->>Engine: analyze AST & resolve Throws<> types
    Engine-->>Plugin: return CheckResult[] (diagnostics)
    Plugin->>LS: append diagnostics (code 90001)
    LS->>Editor: surface augmented diagnostics

    CLI->>Engine: run checkSourceFile over file globs
    Engine-->>CLI: return results
    CLI->>CI: print results and set exit code
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Poem

🐰 I count the bytes, I guard each throw,

I hop through headers where the paddings grow.
A timestamp tucked, an IV snug and neat,
The transformer whispers, "Handle or repeat."
CI hums softly — the burrow's code is sweet.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.68% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main changes: data track packet serialization/deserialization and error handling improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
src/room/data-track/packet/extensions.ts (1)

81-85: Validate IV length up front to avoid unexpected RangeErrors.

lengthBytes assumes a 12‑byte IV; a mismatch will either overflow the DataView or trigger the post-write length check. A constructor guard makes failures immediate and clearer.

♻️ Proposed guard
   constructor(keyIndex: number, iv: Uint8Array) {
     super();
+    if (iv.length !== 12) {
+      throw new Error(
+        `DataTrackE2eeExtension iv must be 12 bytes; got ${iv.length}`,
+      );
+    }
     this.keyIndex = keyIndex;
     this.iv = iv;
   }
📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 16e5f81 and 95d52f4.

📒 Files selected for processing (1)
  • src/room/data-track/packet/extensions.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1798
File: src/room/data-track/packet/extensions.ts:216-242
Timestamp: 2026-01-28T02:22:46.510Z
Learning: In the client-sdk-js codebase at `src/room/data-track/packet/extensions.ts`, RTP length fields represent the actual length in bytes minus 1, following the RTP extension encoding convention.
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.
📚 Learning: 2026-01-28T02:22:46.510Z
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1798
File: src/room/data-track/packet/extensions.ts:216-242
Timestamp: 2026-01-28T02:22:46.510Z
Learning: In the client-sdk-js codebase at `src/room/data-track/packet/extensions.ts`, RTP length fields represent the actual length in bytes minus 1, following the RTP extension encoding convention.

Applied to files:

  • src/room/data-track/packet/extensions.ts
🧬 Code graph analysis (1)
src/room/data-track/packet/extensions.ts (5)
src/index.ts (3)
  • DataTrackUserTimestampExtension (155-155)
  • DataTrackE2eeExtension (156-156)
  • DataTrackExtensions (154-154)
src/room/data-track/packet/constants.ts (4)
  • U16_LENGTH_BYTES (3-3)
  • U64_LENGTH_BYTES (5-5)
  • U8_LENGTH_BYTES (2-2)
  • EXT_TAG_PADDING (27-27)
src/utils/throws.ts (1)
  • Throws (14-14)
src/room/data-track/packet/errors.ts (1)
  • DataTrackDeserializeError (13-74)
src/room/data-track/utils.ts (1)
  • coerceToDataView (72-86)
🪛 Biome (2.1.2)
src/room/data-track/packet/extensions.ts

[error] 224-224: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 226-226: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🔇 Additional comments (8)
src/room/data-track/packet/extensions.ts (8)

1-10: Enum and imports look good.


12-16: Base extension contract is clear and consistent.


18-60: Serialization path is consistent and guarded.


87-122: E2EE binary encoding logic looks solid.


140-157: Constructor + length aggregation are straightforward.


159-183: Binary write order and length validation look good.


253-258: JSON wrapper is clean and consistent.


62-68: The toJSON() method is designed to return the object representation with the bigint value intact. The existing test at src/room/data-track/packet/index.test.ts:496-500 explicitly expects timestamp: 0x4411221111118811n (bigint) in the output. The suggested conversion to string would break this test. Additionally, there is no evidence in the codebase that this toJSON() result is passed to JSON.stringify—it is used for object representation and testing purposes only.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@ladvoc
Copy link
Contributor

ladvoc commented Jan 23, 2026

Just as additional context for other reviewers, the Rust implementation lives in this module.

Copy link
Contributor

@ladvoc ladvoc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serialization/deserialization looks good to me! I will defer to @lukasIO for the details surrounding error handling.

@@ -0,0 +1,25 @@
/** An abstract class implementing common behavior related to data track binary serialization. */
export default abstract class Serializable {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: This is clean.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/room/data-track/packet/index.test.ts`:
- Around line 292-305: The test's expected payload omits the leading 0 byte
present in the actual deserialized/round‑trip data, which hides an offset bug;
update the assertion in the test that calls packet.toJSON() (and the similar
assertion around lines 539-552) to include the leading 0 in the expected payload
Uint8Array (i.e., make the payload new
Uint8Array([0,1,2,3,4,5,6,7,8,9]).buffer), or if the packet is meant to include
an extension-word byte instead, adjust the packet construction/header flags (the
fields used when creating the Packet object and its header such as frameNumber,
marker (FrameMarker.Single), sequence, timestamp, trackHandle, and any extension
flags) so the serialized bytes correctly reflect the absence of that 0; prefer
the first option unless you also change header/flags to explicitly remove the
extension-word.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e60824e and 31a3b58.

📒 Files selected for processing (1)
  • src/room/data-track/packet/index.test.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.
🪛 ESLint
src/room/data-track/packet/index.test.ts

[error] 2-2: Unable to resolve path to module 'vitest'.

(import-x/no-unresolved)

🔇 Additional comments (1)
src/room/data-track/packet/index.test.ts (1)

2-2: Configure ESLint to handle test file imports for devDependencies.

The vitest import triggers import-x/no-unresolved because the ESLint config has no override for test files to allow devDependency imports. Add a test file pattern override to eslint.config.mjs that excludes import-x/no-extraneous-dependencies for .test.ts and .test.js files, or configure the import-x resolver to recognize devDependencies in test contexts.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/room/data-track/packet/index.test.ts`:
- Around line 31-33: The comment on the header byte 0x18 in the test arrays is
incorrect: 0x18 represents "Single" with no extensions, not "final, extension";
update the inline comment(s) that currently read things like "// Version 0,
final, extension" to accurately state "// Single, no extensions" (or similar)
wherever the 0x18 header byte is used in
src/room/data-track/packet/index.test.ts so the comment matches the encoded
marker/extension bits for the Uint8Array test fixtures.
- Line 2: The TypeScript config is missing "vitest" in the compiler "types"
list, causing import resolution failures for test globals like the vitest
imports (afterEach, beforeEach, describe, it, vi) in tests such as
index.test.ts; open your tsconfig.json and add "vitest" to the "types" array
(e.g., include "sdp-transform", "ua-parser-js", "events",
"dom-mediacapture-record", "vitest") so the compiler and eslint import-x
resolver recognize vitest types.
🧹 Nitpick comments (2)
src/room/data-track/packet/index.ts (2)

81-152: Add a capacity guard before writing the header.

Right now an undersized DataView will throw a RangeError mid-write; an explicit length check makes failures deterministic and easier to debug.

♻️ Proposed fix
   toBinaryInto(dataView: DataView): number {
+    const totalLengthBytes = this.toBinaryLengthBytes();
+    if (dataView.byteLength < totalLengthBytes) {
+      throw new Error(
+        `DataTrackPacketHeader.toBinaryInto: DataView too small (${dataView.byteLength} bytes) for ${totalLengthBytes} bytes`,
+      );
+    }
+
     let initial = SUPPORTED_VERSION << VERSION_SHIFT;
@@
-    const totalLengthBytes = this.toBinaryLengthBytes();
     if (byteIndex !== totalLengthBytes) {
       throw new Error(
         `DataTrackPacketHeader.toBinaryInto: Wrote ${byteIndex} bytes but expected length was ${totalLengthBytes} bytes`,
       );
     }

304-313: Prefer a bulk copy for payload serialization.

Writing payload byte-by-byte is correct but slower and noisier than a single Uint8Array#set.

♻️ Proposed fix
-    const payloadBytes = new Uint8Array(this.payload);
-    for (let index = 0; index < payloadBytes.length; index += 1) {
-      dataView.setUint8(byteIndex, payloadBytes[index]);
-      byteIndex += U8_LENGTH_BYTES;
-    }
+    const payloadBytes = new Uint8Array(this.payload);
+    new Uint8Array(
+      dataView.buffer,
+      dataView.byteOffset + byteIndex,
+      payloadBytes.length,
+    ).set(payloadBytes);
+    byteIndex += payloadBytes.length;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31a3b58 and db0fed0.

📒 Files selected for processing (2)
  • src/room/data-track/packet/index.test.ts
  • src/room/data-track/packet/index.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.
🧬 Code graph analysis (2)
src/room/data-track/packet/index.ts (5)
src/room/data-track/handle.ts (2)
  • DataTrackHandle (44-66)
  • DataTrackHandleError (10-42)
src/room/data-track/utils.ts (4)
  • WrapAroundUnsignedInt (9-51)
  • U16_MAX_SIZE (1-1)
  • DataTrackTimestamp (53-70)
  • coerceToDataView (72-86)
src/room/data-track/packet/extensions.ts (1)
  • DataTrackExtensions (131-253)
src/room/data-track/packet/constants.ts (16)
  • BASE_HEADER_LEN (9-9)
  • EXT_WORDS_INDICATOR_SIZE (23-23)
  • SUPPORTED_VERSION (8-8)
  • VERSION_SHIFT (12-12)
  • FRAME_MARKER_INTER (20-20)
  • FRAME_MARKER_FINAL (19-19)
  • FRAME_MARKER_START (18-18)
  • FRAME_MARKER_SINGLE (21-21)
  • FRAME_MARKER_SHIFT (15-15)
  • EXT_FLAG_SHIFT (24-24)
  • U8_LENGTH_BYTES (2-2)
  • U16_LENGTH_BYTES (3-3)
  • U32_LENGTH_BYTES (4-4)
  • VERSION_MASK (13-13)
  • FRAME_MARKER_MASK (16-16)
  • EXT_FLAG_MASK (25-25)
src/room/data-track/packet/errors.ts (2)
  • DataTrackDeserializeErrorAll (76-82)
  • DataTrackDeserializeError (13-74)
src/room/data-track/packet/index.test.ts (6)
src/room/data-track/packet/index.ts (2)
  • DataTrackPacketHeader (32-274)
  • DataTrackPacket (289-343)
src/index.ts (5)
  • DataTrackPacketHeader (152-152)
  • DataTrackPacket (152-152)
  • DataTrackExtensions (154-154)
  • DataTrackUserTimestampExtension (155-155)
  • DataTrackE2eeExtension (156-156)
src/room/data-track/handle.ts (1)
  • DataTrackHandle (44-66)
src/room/data-track/utils.ts (2)
  • WrapAroundUnsignedInt (9-51)
  • DataTrackTimestamp (53-70)
src/room/data-track/packet/extensions.ts (3)
  • DataTrackExtensions (131-253)
  • DataTrackUserTimestampExtension (18-68)
  • DataTrackE2eeExtension (70-129)
src/room/data-track/packet/constants.ts (1)
  • EXT_FLAG_SHIFT (24-24)
🪛 ESLint
src/room/data-track/packet/index.test.ts

[error] 2-2: Unable to resolve path to module 'vitest'.

(import-x/no-unresolved)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (5)
src/room/data-track/packet/index.ts (3)

62-79: Header length + padding math looks solid.

The extensionsMetrics() and toBinaryLengthBytes() logic is clear and consistent with the RTP-style padding rules.


155-262: Deserialization path is clean and well-defended.

Good use of version/length checks and error wrapping for invalid handles.


325-338: Payload slicing respects DataView offsets.

The buffer slice uses byteOffset correctly, which is critical for sliced views.

src/room/data-track/packet/index.test.ts (2)

259-519: Deserialization coverage is thorough and well-scoped.

Nice spread of negative tests (too short, missing ext words, overrun, unsupported version) and extension parsing.


521-580: Round-trip test adds strong confidence.

Good to see encode/decode symmetry validated on a representative packet.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@throws-transformer/README.md`:
- Around line 34-35: Fix the typo in the README: change "Maually" to "Manually"
in the sentence mentioning the Throws<T, E> type so it reads "Manually throwing
of `Error`s is left open as an escape hatch for implementing `panic!()`-like
behavior." Update the line that references the Throws<T, E> type accordingly.
- Around line 87-99: The README example uses the wrong transformer path; update
the JSON snippet so the "transform" value references the actual file
./throws-transformer/transformer.ts (remove the duplicate transformer/ segment)
so the plugin entry { "name": "throws-transformer/transformer", "transform":
"<correct path>" } points to ./throws-transformer/transformer.ts; ensure the
other plugin entry { "name": "./throws-transformer/plugin.ts" } remains
unchanged.
♻️ Duplicate comments (2)
throws-transformer/engine.ts (1)

648-670: Inverted logic in then-block handling.

The thenRethrows condition collects narrowed types when the then-block throws, but the intention is to collect types that are handled (don't throw). Compare with the else-branch at lines 677-679 which correctly uses !containsThrowStatement().

Proposed fix
-    const thenRethrows = ts.isBlock(thenStatement)
+    const thenHandles = ts.isBlock(thenStatement)
+      ? !containsThrowStatement(thenStatement)
+      : !ts.isThrowStatement(thenStatement);
+
+    if (thenHandles) {
+      // Get narrowed type in the then-block
+      const narrowedType = getNarrowedTypeInBlock(
+        thenStatement,
+        errorVarName,
+        checker,
+      );
-      ? containsThrowStatement(thenStatement)
-      : ts.isThrowStatement(thenStatement);
-    
-    if (thenRethrows) {
-      // Get narrowed type in the then-block
-      const narrowedType = getNarrowedTypeInBlock(
-        thenStatement,
-        errorVarName,
-        checker,
-      );
throws-transformer/README.md (1)

136-139: Add a language identifier to the fenced code block.

The diagnostic output block is missing a language tag (MD040).

Suggested fix
-```
+```text
 Unhandled error(s) from 'fetchUser': NetworkError | NotFoundError.
 Catch these errors or add 'Throws<..., NetworkError | NotFoundError>' to your function's return type.
 ```
🧹 Nitpick comments (2)
throws-transformer/engine.ts (1)

1-8: File header appears to be copy-pasted from CLI.

The shebang (#!/usr/bin/env tsx) and doc comment describe a "CLI tool" with usage examples referencing src/cli.ts, but this is engine.ts — a library module, not an executable. Consider updating the header to describe the engine's purpose.

Suggested header update
-#!/usr/bin/env tsx
-/**
- * CLI tool to check TypeScript files for unhandled Throws errors.
- *
- * Usage:
- *   npx ts-node src/cli.ts src/examples.ts
- *   npx ts-node src/cli.ts "src/*.ts"
- */
+/**
+ * Core engine for checking TypeScript files for unhandled Throws<> errors.
+ * Used by the CLI, language service plugin, and transformer.
+ */
src/room/data-track/packet/serializable.ts (1)

12-27: Resolve the FIXME to lock the return type.

Please decide between Uint8Array vs ArrayBuffer and update the method contract + comment accordingly, so the API surface is unambiguous.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db0fed0 and 0e3b838.

📒 Files selected for processing (6)
  • src/room/data-track/packet/errors.ts
  • src/room/data-track/packet/index.test.ts
  • src/room/data-track/packet/index.ts
  • src/room/data-track/packet/serializable.ts
  • throws-transformer/README.md
  • throws-transformer/engine.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.
📚 Learning: 2026-01-23T20:04:34.181Z
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.

Applied to files:

  • throws-transformer/engine.ts
  • throws-transformer/README.md
🧬 Code graph analysis (4)
src/room/data-track/packet/serializable.ts (2)
src/utils/throws.ts (1)
  • Throws (12-12)
src/room/data-track/packet/errors.ts (1)
  • DataTrackSerializeErrorAll (119-121)
src/room/data-track/packet/index.test.ts (6)
src/room/data-track/packet/index.ts (2)
  • DataTrackPacketHeader (38-286)
  • DataTrackPacket (301-359)
src/index.ts (5)
  • DataTrackPacketHeader (152-152)
  • DataTrackPacket (152-152)
  • DataTrackExtensions (154-154)
  • DataTrackUserTimestampExtension (155-155)
  • DataTrackE2eeExtension (156-156)
src/room/data-track/handle.ts (1)
  • DataTrackHandle (44-66)
src/room/data-track/utils.ts (2)
  • WrapAroundUnsignedInt (9-51)
  • DataTrackTimestamp (53-70)
src/room/data-track/packet/extensions.ts (3)
  • DataTrackExtensions (131-253)
  • DataTrackUserTimestampExtension (18-68)
  • DataTrackE2eeExtension (70-129)
src/room/data-track/packet/constants.ts (1)
  • EXT_FLAG_SHIFT (24-24)
src/room/data-track/packet/index.ts (4)
src/room/data-track/handle.ts (2)
  • DataTrackHandle (44-66)
  • DataTrackHandleError (10-42)
src/room/data-track/utils.ts (4)
  • WrapAroundUnsignedInt (9-51)
  • U16_MAX_SIZE (1-1)
  • DataTrackTimestamp (53-70)
  • coerceToDataView (72-86)
src/room/data-track/packet/extensions.ts (1)
  • DataTrackExtensions (131-253)
src/room/data-track/packet/errors.ts (4)
  • DataTrackSerializeError (89-117)
  • DataTrackDeserializeErrorAll (76-82)
  • DataTrackDeserializeError (13-74)
  • DataTrackSerializeErrorAll (119-121)
src/room/data-track/packet/errors.ts (1)
src/room/data-track/handle.ts (1)
  • DataTrackHandleError (10-42)
🪛 ESLint
src/room/data-track/packet/index.test.ts

[error] 2-2: Unable to resolve path to module 'vitest'.

(import-x/no-unresolved)

🪛 LanguageTool
throws-transformer/README.md

[grammar] ~34-~34: Ensure spelling is correct
Context: ... is meant to handle Error subclasses. Maually throwing of Errors is left open as an...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
throws-transformer/README.md

136-136: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (16)
throws-transformer/engine.ts (7)

30-80: LGTM!

The checkSourceFile function provides a clean entry point with a well-structured recursive AST traversal that correctly handles different node types (calls, awaits, throws, returns).


82-135: LGTM!

The checkThrowStatement function correctly validates thrown types against Throws<> declarations with appropriate early exits for non-applicable cases.


137-196: LGTM!

The checkPromiseReject implementation correctly validates rejected types against declared errors. The known limitation around .catch() handler detection was previously discussed and acknowledged as future work.


198-256: LGTM!

The return statement checker properly validates error propagation and provides clear diagnostic messages.


258-425: LGTM!

The helper functions for extracting and comparing error types are well-structured with appropriate null checks and early returns.


427-491: LGTM!

Good design decision to allow Error as an escape hatch for panic-like behavior. The base type resolution correctly handles class inheritance.


723-841: LGTM!

The remaining utility functions are well-implemented with appropriate boundary checks and fallback handling.

throws-transformer/README.md (1)

1-271: Good documentation structure.

The README provides clear setup instructions, usage patterns, and appropriately documents known limitations. The examples align well with the engine implementation.

src/room/data-track/packet/errors.ts (2)

4-82: Clear deserialize error taxonomy and factories.

Enums, reason names, and static constructors are consistent and easy to consume.


84-121: Serialize error definitions look consistent.

The enum + factory pattern mirrors the deserialize side cleanly.

src/room/data-track/packet/index.test.ts (3)

14-289: Good serialization coverage for header + payload layouts.

Covers multiple marker/extension combinations plus size‑error paths.


292-552: Deserialization tests look comprehensive.

Nice coverage of error conditions and extension parsing variants.


554-613: Round‑trip test provides solid regression coverage.

src/room/data-track/packet/index.ts (3)

37-165: Header serialization path looks solid.

Length computation, extension sizing, and capacity checks are well-structured.


167-274: Header deserialization and error mapping look correct.

Clear version/flag handling and clean wrapping of handle errors.


300-358: Packet serialization/deserialization composition is clean.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@src/room/data-track/packet/extensions.ts`:
- Line 94: The generic type parameter list on the toBinaryInto declaration is
using an unnecessary second type argument—change the signature of toBinaryInto
(currently declared as Throws<number, never>) to use only the single required
type parameter (Throws<number>) to remove the redundant `never` and resolve the
CI failure; update the declaration in the toBinaryInto function within
extensions.ts (same pattern as the fix at the earlier occurrence around line 38)
so only the needed type parameter remains.
- Line 38: The method signature for toBinaryInto currently uses the redundant
Throws<number, never> type; remove the explicit never type so it uses the
default error type (e.g. change Throws<number, never> to Throws<number> or
simply Throws<number>) in the toBinaryInto declaration to satisfy CI and keep
types correct; update the toBinaryInto method signature accordingly.
- Line 157: The Throws generic on toBinaryInto is declared with an unnecessary
second type parameter; update the signature from toBinaryInto(dataView:
DataView): Throws<number, never> to remove the redundant `never` (e.g.
toBinaryInto(dataView: DataView): Throws<number>) and apply the same fix to the
other occurrences noted (the other Throws usages referenced in this file) so all
Throws declarations use the correct arity.

In `@throws-transformer/README.md`:
- Around line 124-128: Replace the incorrect CLI examples "npx throws-check
src/myfile.ts" and "npx throws-check src/*.ts" in the README with the actual
available commands: show "npm run throws:check" (or "pnpm throws:check") for the
default check and "npx tsx ./throws-transformer/cli.ts src/myfile.ts" for
checking specific files; update the text around the examples so it references
these exact commands and notes that there is no bin entry for throws-check.
- Around line 253-258: The "Built-in Error Classes" section falsely claims
exports that don't exist; either implement and export concrete error classes
named NetworkError, NotFoundError, ValidationError, and ParseError from the
package (add definitions and export them from the module entry) or update the
README text to remove the claim and clearly state that NetworkError and
NotFoundError are only example snippets and that users must define their own
error types (and remove ValidationError/ParseError or mark them as
placeholders). Ensure references to NetworkError, NotFoundError,
ValidationError, and ParseError in the README match the actual exported symbols
(or are explicitly labeled as examples) so the docs and codebase stay in sync.
♻️ Duplicate comments (4)
src/room/data-track/packet/index.test.ts (2)

1-12: LGTM!

Test imports are well-organized, covering all necessary types from the packet module and test utilities from vitest.

The ESLint import-x/no-unresolved error for vitest is a configuration issue — vitest needs to be added to tsconfig.json types array as noted in a previous review.


31-32: Fix misleading comment: 0x18 has no extension flag set.

0x18 = 0b00011000 encodes Version 0, Single marker, with extension flag = 0. The comment says "extension" which is misleading since there are no extensions in this packet.

🔧 Suggested fix
-          0x18, // Version 0, single, extension
+          0x18, // Version 0, single, no extension

Also applies to lines 298 and 571.

throws-transformer/README.md (1)

152-155: Add a language identifier to the fenced code block.

The diagnostic output block is missing a language tag, which violates markdown linting rules (MD040).

✏️ Proposed fix
-```
+```text
 Unhandled error(s) from 'fetchUser': NetworkError | NotFoundError.
 Catch these errors or add 'Throws<..., NetworkError | NotFoundError>' to your function's return type.
</details>

</blockquote></details>
<details>
<summary>throws-transformer/engine.ts (1)</summary><blockquote>

`662-684`: **Invert then-branch handling check to avoid misclassifying rethrows.**

Line 662-684 currently collects narrowed types when the branch rethrows, which treats rethrown errors as handled and can suppress real diagnostics.  
  

<details>
<summary>🛠️ Proposed fix</summary>

```diff
-    // Check if the then-block handles the error (doesn't re-throw)
-    const thenStatement = ifStmt.thenStatement;
-    const thenRethrows = ts.isBlock(thenStatement)
-      ? containsThrowStatement(thenStatement)
-      : ts.isThrowStatement(thenStatement);
-    
-    if (thenRethrows) {
+    // Check if the then-block handles the error (doesn't re-throw)
+    const thenStatement = ifStmt.thenStatement;
+    const thenHandles = ts.isBlock(thenStatement)
+      ? !containsThrowStatement(thenStatement)
+      : !ts.isThrowStatement(thenStatement);
+    
+    if (thenHandles) {
🧹 Nitpick comments (7)
src/room/data-track/packet/extensions.ts (2)

78-84: Consider validating IV length in constructor.

The comment notes IV should be 12 bytes, but no validation enforces this. If iv.length !== 12, serialization will panic at the length check (lines 111-117) with a confusing message rather than a clear validation error.

🛠️ Suggested validation
 constructor(keyIndex: number, iv: Uint8Array) {
   super();
+  if (iv.length !== 12) {
+    throw new Error(`DataTrackE2eeExtension: IV must be exactly 12 bytes, got ${iv.length}`);
+  }
   this.keyIndex = keyIndex;
   this.iv = iv;
 }

216-233: Wrap switch case in braces to satisfy Biome linter.

Biome flags const keyIndex and const iv declarations (lines 221, 223) as accessible from other switch clauses. While functionally safe here, wrapping in braces is cleaner.

🔧 Suggested fix
-        case DataTrackExtensionTag.E2ee:
+        case DataTrackExtensionTag.E2ee: {
           if (dataView.byteLength - byteIndex < DataTrackE2eeExtension.lengthBytes) {
             throw DataTrackDeserializeError.malformedExt(tag);
           }

           const keyIndex = dataView.getUint8(byteIndex);

           const iv = new Uint8Array(12);
           for (let i = 0; i < iv.length; i += 1) {
             let byteIndexForIv = byteIndex;
             byteIndexForIv += U8_LENGTH_BYTES; // key index
             byteIndexForIv += i * U8_LENGTH_BYTES; // Index into iv array
             iv[i] = dataView.getUint8(byteIndexForIv);
           }

           e2ee = new DataTrackE2eeExtension(keyIndex, iv);
           byteIndex += lengthBytes;
           break;
+        }
src/room/data-track/packet/index.ts (2)

264-274: Non-null assertion is safe but could be clearer.

The trackHandle! assertion at line 267 is safe because the try-catch guarantees either trackHandle is assigned or an exception is thrown. However, restructuring could eliminate the assertion.

🛠️ Optional: Eliminate non-null assertion
-    let trackHandle: DataTrackHandle | undefined;
-    try {
-      trackHandle = DataTrackHandle.fromNumber(dataView.getUint16(byteIndex));
-    } catch (e) {
+    let trackHandle: DataTrackHandle;
+    const rawHandle = dataView.getUint16(byteIndex);
+    try {
+      trackHandle = DataTrackHandle.fromNumber(rawHandle);
+    } catch (e) {
       if (
         e instanceof DataTrackHandleError &&
         (e.isReason(DataTrackHandleErrorReason.Reserved) ||
           e.isReason(DataTrackHandleErrorReason.TooLarge))
       ) {
         throw DataTrackDeserializeError.invalidHandle(e);
-      } else {
-        throw e;
       }
+      throw e;
     }
     byteIndex += U16_LENGTH_BYTES;

     // ...later...
-        trackHandle: trackHandle!,
+        trackHandle,

326-330: Consider using Uint8Array.set() for payload copy.

The byte-by-byte loop works correctly but is slower than using the built-in Uint8Array.set() method for bulk copy.

🛠️ Suggested optimization
-    const payloadBytes = new Uint8Array(this.payload);
-    for (let index = 0; index < payloadBytes.length; index += 1) {
-      dataView.setUint8(byteIndex, payloadBytes[index]);
-      byteIndex += U8_LENGTH_BYTES;
-    }
+    const payloadBytes = new Uint8Array(this.payload);
+    new Uint8Array(dataView.buffer, dataView.byteOffset + byteIndex).set(payloadBytes);
+    byteIndex += payloadBytes.length;
throws-transformer/README.md (2)

34-34: Minor style refinement: simplify "inside of" to "inside".

The phrase "throw inside of a function" can be simplified to "throw inside a function" for more concise writing.

✏️ Proposed fix
-If you have a situation where you would like to throw inside of a function annotated with a `Throws`
+If you have a situation where you would like to throw inside a function annotated with a `Throws`

283-283: Clarify the scope of the "VS Code only" limitation.

The statement "VS Code only: The language service plugin is VS Code specific" is misleading. While the language service plugin for real-time squiggles is VS Code specific, the README also describes ts-patch integration (lines 92-118) and a CLI checker (lines 119-129), both of which work outside VS Code. Consider clarifying that only the editor integration is VS Code specific, while build-time and CLI checking work in any environment.

📝 Proposed clarification
-3. **VS Code only**: The language service plugin is VS Code specific (other editors may vary)
+3. **Editor integration**: The language service plugin is VS Code specific (other editors may vary). However, build-time checking via ts-patch and the CLI checker work in any environment.
throws-transformer/engine.ts (1)

625-631: Skip method/accessor/constructor declarations when scanning for rethrows.

The “nested functions” guard omits methods/accessors/constructors, so throws inside those nested scopes can incorrectly mark the catch as rethrowing.

♻️ Suggested adjustment
-    if (
-      ts.isFunctionDeclaration(node) ||
-      ts.isFunctionExpression(node) ||
-      ts.isArrowFunction(node)
-    ) {
+    if (
+      ts.isFunctionDeclaration(node) ||
+      ts.isFunctionExpression(node) ||
+      ts.isArrowFunction(node) ||
+      ts.isMethodDeclaration(node) ||
+      ts.isConstructorDeclaration(node) ||
+      ts.isGetAccessorDeclaration(node) ||
+      ts.isSetAccessorDeclaration(node)
+    ) {
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3b838 and 3e61967.

📒 Files selected for processing (6)
  • src/room/data-track/packet/extensions.ts
  • src/room/data-track/packet/index.test.ts
  • src/room/data-track/packet/index.ts
  • src/room/data-track/packet/serializable.ts
  • throws-transformer/README.md
  • throws-transformer/engine.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/room/data-track/packet/serializable.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.
📚 Learning: 2026-01-23T20:04:34.181Z
Learnt from: 1egoman
Repo: livekit/client-sdk-js PR: 1792
File: tsconfig.json:29-29
Timestamp: 2026-01-23T20:04:34.181Z
Learning: In the livekit/client-sdk-js repository, the throws-transformer TypeScript language service plugin is configured in tsconfig.json using a relative file path `"./throws-transformer/plugin.ts"` rather than a package name. This file path configuration is intentional and valid for VS Code when using workspace TypeScript versions.

Applied to files:

  • throws-transformer/engine.ts
  • throws-transformer/README.md
🧬 Code graph analysis (2)
src/room/data-track/packet/index.ts (5)
src/room/data-track/handle.ts (2)
  • DataTrackHandle (44-66)
  • DataTrackHandleError (10-42)
src/room/data-track/utils.ts (4)
  • WrapAroundUnsignedInt (9-51)
  • U16_MAX_SIZE (1-1)
  • DataTrackTimestamp (53-70)
  • coerceToDataView (72-86)
src/room/data-track/packet/extensions.ts (1)
  • DataTrackExtensions (133-256)
src/room/data-track/packet/constants.ts (16)
  • BASE_HEADER_LEN (9-9)
  • EXT_WORDS_INDICATOR_SIZE (23-23)
  • SUPPORTED_VERSION (8-8)
  • VERSION_SHIFT (12-12)
  • FRAME_MARKER_INTER (20-20)
  • FRAME_MARKER_FINAL (19-19)
  • FRAME_MARKER_START (18-18)
  • FRAME_MARKER_SINGLE (21-21)
  • FRAME_MARKER_SHIFT (15-15)
  • EXT_FLAG_SHIFT (24-24)
  • U8_LENGTH_BYTES (2-2)
  • U16_LENGTH_BYTES (3-3)
  • U32_LENGTH_BYTES (4-4)
  • VERSION_MASK (13-13)
  • FRAME_MARKER_MASK (16-16)
  • EXT_FLAG_MASK (25-25)
src/room/data-track/packet/errors.ts (3)
  • DataTrackSerializeError (89-117)
  • DataTrackDeserializeErrorAll (76-82)
  • DataTrackDeserializeError (13-74)
src/room/data-track/packet/extensions.ts (5)
src/index.ts (3)
  • DataTrackUserTimestampExtension (155-155)
  • DataTrackE2eeExtension (156-156)
  • DataTrackExtensions (154-154)
src/room/data-track/packet/constants.ts (4)
  • U16_LENGTH_BYTES (3-3)
  • U64_LENGTH_BYTES (5-5)
  • U8_LENGTH_BYTES (2-2)
  • EXT_TAG_PADDING (27-27)
src/utils/throws.ts (1)
  • Throws (12-12)
src/room/rpc.ts (1)
  • byteLength (150-153)
src/room/data-track/packet/errors.ts (1)
  • DataTrackDeserializeError (13-74)
🪛 Biome (2.1.2)
src/room/data-track/packet/extensions.ts

[error] 221-221: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 223-223: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

🪛 ESLint
src/room/data-track/packet/index.test.ts

[error] 2-2: Unable to resolve path to module 'vitest'.

(import-x/no-unresolved)

🪛 GitHub Actions: Test
src/room/data-track/packet/extensions.ts

[error] 38-38: eslint: This is the default value for this type parameter, so it can be omitted. (no-unnecessary-type-arguments)

🪛 GitHub Check: test
src/room/data-track/packet/extensions.ts

[failure] 157-157:
This is the default value for this type parameter, so it can be omitted


[failure] 94-94:
This is the default value for this type parameter, so it can be omitted


[failure] 38-38:
This is the default value for this type parameter, so it can be omitted

🪛 LanguageTool
throws-transformer/README.md

[style] ~34-~34: This phrase is redundant. Consider using “inside”.
Context: ...situation where you would like to throw inside of a function annotated with a Throws ty...

(OUTSIDE_OF)

🪛 markdownlint-cli2 (0.18.1)
throws-transformer/README.md

152-152: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (13)
src/room/data-track/packet/extensions.ts (2)

1-69: LGTM!

The DataTrackUserTimestampExtension implementation is well-structured with proper static properties, clear serialization logic, and appropriate panic checks for invariant violations.


133-181: LGTM!

The DataTrackExtensions container properly aggregates extensions, handles optional fields, and maintains serialization order consistency between toBinaryInto and toBinaryLengthBytes.

src/room/data-track/packet/index.ts (5)

1-36: LGTM!

Clean imports with proper organization. The use of coerceToDataView from utils ensures proper handling of Uint8Array slice offsets.


208-221: Good error wrapping pattern.

The catch-and-rethrow pattern properly converts DataTrackHandleError to DataTrackDeserializeError while preserving the cause chain. This provides clear error semantics at the API boundary.


343-356: LGTM!

The fromBinary method correctly uses coerceToDataView for proper slice handling and computes payload bounds using absolute offsets (dataView.byteOffset + ...).


87-166: LGTM!

The toBinaryInto method properly validates buffer capacity, handles extension word encoding with RTP-oriented semantics (length = words - 1), and includes a panic check for invariant validation.


289-299: LGTM!

Clear enum documentation for FrameMarker values explaining each packet position within a frame.

src/room/data-track/packet/index.test.ts (6)

14-57: LGTM!

Serialization test for single packet correctly validates header fields, binary length, and exact byte layout.


58-164: LGTM!

Comprehensive test for final packet with both e2ee and userTimestamp extensions. Validates extension encoding, word alignment, padding, and payload placement.


258-289: LGTM!

Good error case coverage testing serialization failures when the DataView is too small for header or payload.


340-376: LGTM!

Error case tests properly validate deserialization failures for too-short buffers, missing extension indicators, header overruns, and unsupported versions.


413-427: LGTM!

Good parameterized test using it.each to verify extension padding is properly skipped for different extension word counts.


554-613: LGTM!

Round-trip test validates serialization-then-deserialization consistency, ensuring data integrity through the full encode/decode cycle.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: is the plan to move this outside of this repo eventually?

Copy link
Contributor Author

@1egoman 1egoman Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it probably makes sense, yea. For now, I kept it vendored in here just to increase iteration speed and because the repo you made with this was private / I didn't have permission to make it public. But I could see it becoming a helper package like @livekit/mutex / etc once it is a bit more stable.

If you have a stronger opinion about the timeline of that move though feel free to mention it here. My preference in a vaccum I think would be to wait to break it out until the static analysis checking script is a bit more robust and some other folks (at least you) have been able to give ita try and weigh in on how it works.

@lukasIO
Copy link
Contributor

lukasIO commented Jan 27, 2026

It would be nice to include an example that makes use of the data tracks in the /examples folder

@1egoman
Copy link
Contributor Author

1egoman commented Jan 27, 2026

It would be nice to include an example that makes use of the data tracks in the /examples folder

@lukasIO The packet serialization / deserialization code isn't something that a user would call directly, so I don't think it makes sense to add an example using it. That being said, once all the data tracks code is complete, I 100% agree, there should be an example deomonstrating end to end use, but I will include that in a follow up change once I've made more progress on the abstractions and it is all integrated with Room.

…p format

And fix an off by one error along the way.
@1egoman 1egoman merged commit c49f048 into main Jan 28, 2026
7 checks passed
@1egoman 1egoman deleted the data-tracks branch January 28, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants