Skip to content
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
27d979b
feat: add initial data track serialization logic
1egoman Jan 21, 2026
2d502f5
refactor: break up data track serialization logic into multiple files
1egoman Jan 21, 2026
eab7d35
feat: add data track packet serialization test
1egoman Jan 21, 2026
3301fa6
fix: remove unused variable
1egoman Jan 21, 2026
c31d29a
feat: do a first pass at deserializing
1egoman Jan 21, 2026
bcdebe8
feat: try to get TypedPromise to work with data tracks
1egoman Jan 21, 2026
158dbca
feat: try out the throws transformer prototype
1egoman Jan 21, 2026
87fb7bd
feat: add throws annotations to all deserialize functions
1egoman Jan 21, 2026
6c76472
fix: address bugs in data tracks deserialization
1egoman Jan 22, 2026
21fd7f4
fix: move toBinaryLengthBytes to address type error
1egoman Jan 22, 2026
3ff0dba
feat: add initial deserialization test
1egoman Jan 22, 2026
a3f258a
feat: add error handling / rethrowing logic
1egoman Jan 22, 2026
76346a4
fix: commit missing handle file
1egoman Jan 22, 2026
b91e810
feat: add explicit LivekitReasonedError abstract type
1egoman Jan 22, 2026
6f231cf
fix: run npm run format
1egoman Jan 22, 2026
369be62
feat: add initial set of deserialization tests
1egoman Jan 22, 2026
4d58c76
feat: add remaining tests
1egoman Jan 23, 2026
b182399
fix: swap fixed sizes for constants per jacob suggestion
1egoman Jan 23, 2026
afb8762
fix: address transposition typo
1egoman Jan 23, 2026
b78f8b0
feat: add track handle tests
1egoman Jan 23, 2026
da94a36
feat: add throws check command
1egoman Jan 23, 2026
ee3a265
feat: add DataTrackDeserializeErrorAll
1egoman Jan 23, 2026
1dfd777
fix: remove unused import
1egoman Jan 23, 2026
b308e54
feat: vendor throws transformer into repo
1egoman Jan 23, 2026
9f7ce0c
feat: add throws transformer check to ci
1egoman Jan 23, 2026
d43c5ed
fix: run npm run format
1egoman Jan 23, 2026
d96affa
fix: remove accidentally committed type
1egoman Jan 23, 2026
3a170e6
fix: run npm run format
1egoman Jan 23, 2026
1534a41
fix: get rid of ErrorOptions
1egoman Jan 23, 2026
bdcc6b1
docs: fix docs comment
1egoman Jan 23, 2026
591a897
fix: try using full es2022 to see if that makes the build work
1egoman Jan 23, 2026
e653c07
Revert "fix: try using full es2022 to see if that makes the build work"
1egoman Jan 23, 2026
d385b80
feat: assign to this.cause directly to bypass build error
1egoman Jan 23, 2026
1f65c42
feat: add cause property to error
1egoman Jan 23, 2026
527339f
fix: make cause optional
1egoman Jan 23, 2026
d6c0624
fix: replace missed ErrorOptions reference
1egoman Jan 23, 2026
962c3da
fix: use length tags in more places
1egoman Jan 23, 2026
04d8731
feat: apply workaround due to sfu bug
1egoman Jan 23, 2026
c4adff4
fix: convert tag / lengthBytes fully into static members
1egoman Jan 23, 2026
1eace3b
feat: add coerceToDataView function to get fromBinary input as a data…
1egoman Jan 23, 2026
2ca4051
fix: address typo in tests
1egoman Jan 23, 2026
02ced9b
fix: adjust extension byte processing to try to make code clearer
1egoman Jan 23, 2026
e5ef502
fix: address potential out of bounds DataView read
1egoman Jan 23, 2026
9507b4d
fix: address potential out of bounds DataView read
1egoman Jan 23, 2026
ff7607e
feat: add WrapAroundUnsignedInt test
1egoman Jan 23, 2026
0df397e
fix: address cause check being backwards
1egoman Jan 23, 2026
e7a3353
fix: remove no-op ts-expect-error
1egoman Jan 23, 2026
a4bca2a
fix: update incorrect throws transformer docs
1egoman Jan 23, 2026
f6b7185
fix: run npm run format
1egoman Jan 23, 2026
603a9f7
fix: address swapped variable names
1egoman Jan 23, 2026
5a2d28a
fix: address error message mispelling
1egoman Jan 23, 2026
007cd5f
feat: add negative wraparound to WrapAroundUnsignedInt
1egoman Jan 23, 2026
600b01c
fix: address docstring typo
1egoman Jan 23, 2026
9c238a2
feat: adjust wrap around behavior to use 1 indexed size not 0 indexed…
1egoman Jan 23, 2026
6cc18bd
feat: handle tsconfig parse error
1egoman Jan 23, 2026
58ada38
fix: add curly braces to blocks
1egoman Jan 23, 2026
cdb0a53
fix: rename variable
1egoman Jan 23, 2026
3b0c0ea
fix: add changeset
1egoman Jan 23, 2026
5fb382d
fix: handle explicitly tsconfig parsing errors
1egoman Jan 23, 2026
e60824e
fix: update test to use 65536
1egoman Jan 23, 2026
31a3b58
feat: add round trip serialize + deserialize test
1egoman Jan 25, 2026
50dc51e
fix: adjust off by one extenson words value based on jacob review com…
1egoman Jan 25, 2026
db0fed0
fix: address off by one error in deserialization of single packet case
1egoman Jan 25, 2026
491c032
feat: add data track serialization errors that were missed the first …
1egoman Jan 25, 2026
6e0b625
fix: remove async from test cases
1egoman Jan 25, 2026
a77f80c
feat: add explicit Error skipping in Throws brand implementation
1egoman Jan 25, 2026
0e3b838
fix: run npm run format
1egoman Jan 25, 2026
d5fcad9
fix: address incorrect comment
1egoman Jan 25, 2026
02545e7
feat: replace explicit ignoring of Error with @throws-transformer ign…
1egoman Jan 25, 2026
c48cda2
feat: use new @throws-transformer ignore syntax instead of explicitly…
1egoman Jan 25, 2026
3e61967
fix: correct docs typo
1egoman Jan 25, 2026
6c1678e
fix: remove default Error param
1egoman Jan 25, 2026
49aeae5
docs: update readme examples
1egoman Jan 25, 2026
16e5f81
fix: remove LLM insertion of a different license
1egoman Jan 25, 2026
95d52f4
fix: make it clearer that the extension length is ALSO stored in a rt…
1egoman Jan 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/busy-ends-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'livekit-client': patch
---

Add data track packet serialization + deserialization logic
5 changes: 4 additions & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ jobs:
- name: Prettier
run: pnpm format:check

- name: Throws check
run: pnpm throws:check

- name: Run Tests
run: pnpm test

- name: Check browser target compatibility
run: |
pnpm build:clean
pnpm compat
pnpm compat
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"deploy": "gh-pages -d examples/demo/dist",
"format": "prettier --write src examples/**/*.ts",
"format:check": "prettier --check src examples/**/*.ts",
"throws:check": "pnpm dlx tsx ./throws-transformer/cli.ts 'src/!(*.test).ts' 'src/**/!(*.test).ts'",
"ci:publish": "pnpm build:clean && pnpm compat && changeset publish",
"downlevel-dts": "downlevel-dts ./dist/src ./dist/ts4.2 --to=4.2",
"compat": "eslint --config ./eslint.config.dist.mjs --no-inline-config ./dist/livekit-client.esm.mjs",
Expand Down Expand Up @@ -98,6 +99,7 @@
"eslint-plugin-import-x": "^4.16.1",
"eslint-plugin-prettier": "^5.5.4",
"gh-pages": "6.3.0",
"glob": "^13.0.0",
"happy-dom": "^17.2.0",
"jsdom": "^26.1.0",
"prettier": "^3.4.2",
Expand Down
67 changes: 61 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,11 @@ export type {
ReconnectPolicy,
RoomEventCallbacks,
};
export { DataTrackPacket, type DataTrackPacketHeader } from './room/data-track/packet';
export {
type DataTrackExtensions,
type DataTrackUserTimestampExtension,
type DataTrackE2eeExtension,
} from './room/data-track/packet/extensions';

export { LocalTrackRecorder } from './room/track/record';
13 changes: 13 additions & 0 deletions src/room/data-track/handle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { DataTrackHandle } from './handle';

describe('DataTrackHandle', () => {
it('should parse handle raw inputs', () => {
expect(DataTrackHandle.fromNumber(3).value).toEqual(3);
expect(() => DataTrackHandle.fromNumber(0)).toThrow('0x0 is a reserved value');
expect(() => DataTrackHandle.fromNumber(9999999)).toThrow(
'Value too large to be a valid track handle',
);
});
});
80 changes: 80 additions & 0 deletions src/room/data-track/handle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { type Throws } from '../../utils/throws';
import { LivekitReasonedError } from '../errors';
import { U16_MAX_SIZE } from './utils';

export enum DataTrackHandleErrorReason {
Reserved = 0,
TooLarge = 1,
}

export class DataTrackHandleError<
Reason extends DataTrackHandleErrorReason = DataTrackHandleErrorReason,
> extends LivekitReasonedError<DataTrackHandleErrorReason> {
readonly name = 'DataTrackHandleError';

reason: Reason;

reasonName: string;

constructor(message: string, reason: Reason) {
super(19, message);
this.reason = reason;
this.reasonName = DataTrackHandleErrorReason[reason];
}

isReason<R extends DataTrackHandleErrorReason>(reason: R): this is DataTrackHandleError<R> {
return (this.reason as unknown as R) === reason;
}

static tooLarge() {
return new DataTrackHandleError(
'Value too large to be a valid track handle',
DataTrackHandleErrorReason.TooLarge,
);
}

static reserved(value: number) {
return new DataTrackHandleError(
`0x${value.toString(16)} is a reserved value.`,
DataTrackHandleErrorReason.Reserved,
);
}
}

export class DataTrackHandle {
public value: number;

static fromNumber(
raw: number,
): Throws<
DataTrackHandle,
| DataTrackHandleError<DataTrackHandleErrorReason.TooLarge>
| DataTrackHandleError<DataTrackHandleErrorReason.Reserved>
> {
if (raw === 0) {
throw DataTrackHandleError.reserved(raw);
}
if (raw > U16_MAX_SIZE) {
throw DataTrackHandleError.tooLarge();
}
return new DataTrackHandle(raw);
}

constructor(raw: number) {
this.value = raw;
}
}

/** Manage allocating new handles which don't conflict over the lifetime of the client. */
export class DataTrackHandleAllocator {
static value = 0;

/** Returns a unique track handle for the next publication, if one can be obtained. */
static get(): DataTrackHandle | null {
this.value += 1;
if (this.value > U16_MAX_SIZE) {
return null;
}
return new DataTrackHandle(this.value);
}
}
27 changes: 27 additions & 0 deletions src/room/data-track/packet/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Number type sizes
export const U8_LENGTH_BYTES = 1;
export const U16_LENGTH_BYTES = 2;
export const U32_LENGTH_BYTES = 4;
export const U64_LENGTH_BYTES = 8;

/// Constants used for serialization and deserialization.
export const SUPPORTED_VERSION = 0;
export const BASE_HEADER_LEN = 12;

// Bitfield shifts and masks for header flags
export const VERSION_SHIFT = 5;
export const VERSION_MASK = 0x07;

export const FRAME_MARKER_SHIFT = 3;
export const FRAME_MARKER_MASK = 0x3;

export const FRAME_MARKER_START = 0x2;
export const FRAME_MARKER_FINAL = 0x1;
export const FRAME_MARKER_INTER = 0x0;
export const FRAME_MARKER_SINGLE = 0x3;

export const EXT_WORDS_INDICATOR_SIZE = 2;
export const EXT_FLAG_SHIFT = 0x2;
export const EXT_FLAG_MASK = 0x1;
export const EXT_MARKER_LEN = 4;
export const EXT_TAG_PADDING = 0;
Loading
Loading