commit
f05bd5a0ac
|
@ -1,4 +1,4 @@
|
||||||
name: Rust
|
name: server
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
@ -10,14 +10,14 @@ env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
check:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Build
|
- name: build
|
||||||
working-directory: server
|
working-directory: server
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
- name: Run tests
|
- name: test
|
||||||
working-directory: server
|
working-directory: server
|
||||||
run: cargo test --verbose
|
run: cargo test --verbose
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
name: web
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: install
|
||||||
|
working-directory: web
|
||||||
|
run: yarn install
|
||||||
|
- name: cert
|
||||||
|
working-directory: cert
|
||||||
|
run: ./generate
|
||||||
|
- name: build
|
||||||
|
working-directory: web
|
||||||
|
run: yarn build
|
||||||
|
- name: lint
|
||||||
|
working-directory: web
|
||||||
|
run: yarn lint
|
|
@ -9,5 +9,14 @@ module.exports = {
|
||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"no-unused-vars": "off", // note you must disable the base rule as it can report incorrect errors
|
||||||
|
"@typescript-eslint/no-unused-vars": [
|
||||||
|
"warn", // or "error"
|
||||||
|
{
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"caughtErrorsIgnorePattern": "^_"
|
||||||
|
}
|
||||||
|
],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,17 +4,21 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "parcel serve --https --cert ../cert/localhost.crt --key ../cert/localhost.key --port 4444 --open",
|
"serve": "parcel serve --https --cert ../cert/localhost.crt --key ../cert/localhost.key --port 4444 --open",
|
||||||
"build": "parcel build",
|
"build": "parcel build",
|
||||||
"check": "tsc --noEmit"
|
"check": "tsc --noEmit",
|
||||||
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-inline-string": "2.8.3",
|
"@parcel/transformer-inline-string": "2.8.3",
|
||||||
"@parcel/validator-typescript": "^2.6.0",
|
"@parcel/validator-typescript": "^2.6.0",
|
||||||
"@types/audioworklet": "^0.0.41",
|
"@types/audioworklet": "^0.0.41",
|
||||||
"@types/dom-webcodecs": "^0.1.6",
|
"@types/dom-webcodecs": "^0.1.6",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
|
"@typescript-eslint/parser": "^5.59.7",
|
||||||
|
"eslint": "^8.41.0",
|
||||||
"parcel": "^2.8.0",
|
"parcel": "^2.8.0",
|
||||||
"typescript": ">=3.0.0"
|
"typescript": "^5.0.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mp4box": "^0.5.2"
|
"mp4box": "^0.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ export class Encoder {
|
||||||
dts: frame.timestamp,
|
dts: frame.timestamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = this.container.createSingleSampleMoof(sample);
|
const _stream = this.container.createSingleSampleMoof(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
onVideo(frame: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata) {
|
onVideo(frame: EncodedVideoChunk, metadata?: EncodedVideoChunkMetadata) {
|
||||||
|
@ -99,6 +99,6 @@ export class Encoder {
|
||||||
dts: frame.timestamp,
|
dts: frame.timestamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stream = this.container.createSingleSampleMoof(sample);
|
const _stream = this.container.createSingleSampleMoof(sample);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
export default class Broadcaster {
|
export default class Broadcaster {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import Transport from "./transport"
|
||||||
import fingerprintHex from 'bundle-text:../fingerprint.hex';
|
import fingerprintHex from 'bundle-text:../fingerprint.hex';
|
||||||
|
|
||||||
// Convert the hex to binary.
|
// Convert the hex to binary.
|
||||||
let fingerprint = [];
|
const fingerprint = [];
|
||||||
for (let c = 0; c < fingerprintHex.length - 1; c += 2) {
|
for (let c = 0; c < fingerprintHex.length - 1; c += 2) {
|
||||||
fingerprint.push(parseInt(fingerprintHex.substring(c, c + 2), 16));
|
fingerprint.push(parseInt(fingerprintHex.substring(c, c + 2), 16));
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,11 @@ export class InitParser {
|
||||||
|
|
||||||
push(data: Uint8Array) {
|
push(data: Uint8Array) {
|
||||||
// Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately
|
// Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately
|
||||||
let box = new Uint8Array(data.byteLength);
|
const box = new Uint8Array(data.byteLength);
|
||||||
box.set(data)
|
box.set(data)
|
||||||
|
|
||||||
// and for some reason we need to modify the underlying ArrayBuffer with fileStart
|
// and for some reason we need to modify the underlying ArrayBuffer with fileStart
|
||||||
let buffer = box.buffer as MP4.ArrayBuffer
|
const buffer = box.buffer as MP4.ArrayBuffer
|
||||||
buffer.fileStart = this.offset
|
buffer.fileStart = this.offset
|
||||||
|
|
||||||
// Parse the data
|
// Parse the data
|
||||||
|
|
|
@ -121,10 +121,10 @@ declare module "mp4box" {
|
||||||
mapUint8Array(length: number): Uint8Array;
|
mapUint8Array(length: number): Uint8Array;
|
||||||
readInt32Array(length: number, littleEndian: boolean): Int32Array;
|
readInt32Array(length: number, littleEndian: boolean): Int32Array;
|
||||||
readInt16Array(length: number, littleEndian: boolean): Int16Array;
|
readInt16Array(length: number, littleEndian: boolean): Int16Array;
|
||||||
readInt8(length: number): Int8Array;
|
readInt8Array(length: number): Int8Array;
|
||||||
readUint32Array(length: number, littleEndian: boolean): Uint32Array;
|
readUint32Array(length: number, littleEndian: boolean): Uint32Array;
|
||||||
readUint16Array(length: number, littleEndian: boolean): Uint16Array;
|
readUint16Array(length: number, littleEndian: boolean): Uint16Array;
|
||||||
readUint8(length: number): Uint8Array;
|
readUint8Array(length: number): Uint8Array;
|
||||||
readFloat64Array(length: number, littleEndian: boolean): Float64Array;
|
readFloat64Array(length: number, littleEndian: boolean): Float64Array;
|
||||||
readFloat32Array(length: number, littleEndian: boolean): Float32Array;
|
readFloat32Array(length: number, littleEndian: boolean): Float32Array;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ export default class Audio {
|
||||||
render?: number; // non-zero if requestAnimationFrame has been called
|
render?: number; // non-zero if requestAnimationFrame has been called
|
||||||
last?: number; // the timestamp of the last rendered frame, in microseconds
|
last?: number; // the timestamp of the last rendered frame, in microseconds
|
||||||
|
|
||||||
constructor(config: Message.Config) {
|
constructor(_config: Message.Config) {
|
||||||
this.queue = []
|
this.queue = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ export default class Audio {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (this.queue.length) {
|
while (this.queue.length) {
|
||||||
let frame = this.queue[0];
|
const frame = this.queue[0];
|
||||||
if (ring.size() + frame.numberOfFrames > ring.capacity) {
|
if (ring.size() + frame.numberOfFrames > ring.capacity) {
|
||||||
// Buffer is full
|
// Buffer is full
|
||||||
break
|
break
|
||||||
|
|
|
@ -16,8 +16,8 @@ export default class Decoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
async receiveInit(msg: Message.Init) {
|
async receiveInit(msg: Message.Init) {
|
||||||
let stream = new Stream.Reader(msg.reader, msg.buffer);
|
const stream = new Stream.Reader(msg.reader, msg.buffer);
|
||||||
while (1) {
|
for (;;) {
|
||||||
const data = await stream.read()
|
const data = await stream.read()
|
||||||
if (!data) break
|
if (!data) break
|
||||||
|
|
||||||
|
@ -29,14 +29,14 @@ export default class Decoder {
|
||||||
|
|
||||||
async receiveSegment(msg: Message.Segment) {
|
async receiveSegment(msg: Message.Segment) {
|
||||||
// Wait for the init segment to be fully received and parsed
|
// Wait for the init segment to be fully received and parsed
|
||||||
const info = await this.init.info
|
const init = await this.init.info
|
||||||
const input = MP4.New();
|
const input = MP4.New();
|
||||||
|
|
||||||
input.onSamples = this.onSamples.bind(this);
|
input.onSamples = this.onSamples.bind(this);
|
||||||
input.onReady = (info: any) => {
|
input.onReady = (track: any) => {
|
||||||
// Extract all of the tracks, because we don't know if it's audio or video.
|
// Extract all of the tracks, because we don't know if it's audio or video.
|
||||||
for (let track of info.tracks) {
|
for (const i of init.tracks) {
|
||||||
input.setExtractionOptions(track.id, track, { nbSamples: 1 });
|
input.setExtractionOptions(track.id, i, { nbSamples: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
input.start();
|
input.start();
|
||||||
|
@ -45,7 +45,7 @@ export default class Decoder {
|
||||||
// MP4box requires us to reparse the init segment unfortunately
|
// MP4box requires us to reparse the init segment unfortunately
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
|
||||||
for (let raw of this.init.raw) {
|
for (const raw of this.init.raw) {
|
||||||
raw.fileStart = offset
|
raw.fileStart = offset
|
||||||
offset = input.appendBuffer(raw)
|
offset = input.appendBuffer(raw)
|
||||||
}
|
}
|
||||||
|
@ -61,11 +61,11 @@ export default class Decoder {
|
||||||
const atom = await stream.bytes(size)
|
const atom = await stream.bytes(size)
|
||||||
|
|
||||||
// Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately
|
// Make a copy of the atom because mp4box only accepts an ArrayBuffer unfortunately
|
||||||
let box = new Uint8Array(atom.byteLength);
|
const box = new Uint8Array(atom.byteLength);
|
||||||
box.set(atom)
|
box.set(atom)
|
||||||
|
|
||||||
// and for some reason we need to modify the underlying ArrayBuffer with offset
|
// and for some reason we need to modify the underlying ArrayBuffer with offset
|
||||||
let buffer = box.buffer as MP4.ArrayBuffer
|
const buffer = box.buffer as MP4.ArrayBuffer
|
||||||
buffer.fileStart = offset
|
buffer.fileStart = offset
|
||||||
|
|
||||||
// Parse the data
|
// Parse the data
|
||||||
|
@ -79,7 +79,7 @@ export default class Decoder {
|
||||||
|
|
||||||
if (!decoder) {
|
if (!decoder) {
|
||||||
// We need a sample to initalize the video decoder, because of mp4box limitations.
|
// We need a sample to initalize the video decoder, because of mp4box limitations.
|
||||||
let sample = samples[0];
|
const sample = samples[0];
|
||||||
|
|
||||||
if (isVideoTrack(track)) {
|
if (isVideoTrack(track)) {
|
||||||
// Configure the decoder using the AVC box for H.264
|
// Configure the decoder using the AVC box for H.264
|
||||||
|
@ -124,7 +124,7 @@ export default class Decoder {
|
||||||
this.decoders.set(track_id, decoder)
|
this.decoders.set(track_id, decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let sample of samples) {
|
for (const sample of samples) {
|
||||||
// Convert to microseconds
|
// Convert to microseconds
|
||||||
const timestamp = 1000 * 1000 * sample.dts / sample.timescale
|
const timestamp = 1000 * 1000 * sample.dts / sample.timescale
|
||||||
const duration = 1000 * 1000 * sample.duration / sample.timescale
|
const duration = 1000 * 1000 * sample.duration / sample.timescale
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class Player {
|
||||||
return worker
|
return worker
|
||||||
}
|
}
|
||||||
|
|
||||||
private async setupWorklet(config: Config): Promise<AudioWorkletNode> {
|
private async setupWorklet(_config: Config): Promise<AudioWorkletNode> {
|
||||||
// Load the worklet source code.
|
// Load the worklet source code.
|
||||||
const url = new URL('worklet.ts', import.meta.url)
|
const url = new URL('worklet.ts', import.meta.url)
|
||||||
await this.context.audioWorklet.addModule(url)
|
await this.context.audioWorklet.addModule(url)
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class Ring {
|
||||||
this.state = new Int32Array(buffer.state)
|
this.state = new Int32Array(buffer.state)
|
||||||
|
|
||||||
this.channels = []
|
this.channels = []
|
||||||
for (let channel of buffer.channels) {
|
for (const channel of buffer.channels) {
|
||||||
this.channels.push(new Float32Array(channel))
|
this.channels.push(new Float32Array(channel))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,8 @@ export class Ring {
|
||||||
|
|
||||||
// Write samples for single audio frame, returning the total number written.
|
// Write samples for single audio frame, returning the total number written.
|
||||||
write(frame: AudioData): number {
|
write(frame: AudioData): number {
|
||||||
let readPos = Atomics.load(this.state, STATE.READ_POS)
|
const readPos = Atomics.load(this.state, STATE.READ_POS)
|
||||||
let writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
const writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
||||||
|
|
||||||
const startPos = writePos
|
const startPos = writePos
|
||||||
let endPos = writePos + frame.numberOfFrames;
|
let endPos = writePos + frame.numberOfFrames;
|
||||||
|
@ -60,8 +60,8 @@ export class Ring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let startIndex = startPos % this.capacity;
|
const startIndex = startPos % this.capacity;
|
||||||
let endIndex = endPos % this.capacity;
|
const endIndex = endPos % this.capacity;
|
||||||
|
|
||||||
// Loop over each channel
|
// Loop over each channel
|
||||||
for (let i = 0; i < this.channels.length; i += 1) {
|
for (let i = 0; i < this.channels.length; i += 1) {
|
||||||
|
@ -102,10 +102,10 @@ export class Ring {
|
||||||
}
|
}
|
||||||
|
|
||||||
read(dst: Float32Array[]): number {
|
read(dst: Float32Array[]): number {
|
||||||
let readPos = Atomics.load(this.state, STATE.READ_POS)
|
const readPos = Atomics.load(this.state, STATE.READ_POS)
|
||||||
let writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
const writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
||||||
|
|
||||||
let startPos = readPos;
|
const startPos = readPos;
|
||||||
let endPos = startPos + dst[0].length;
|
let endPos = startPos + dst[0].length;
|
||||||
|
|
||||||
if (endPos > writePos) {
|
if (endPos > writePos) {
|
||||||
|
@ -116,8 +116,8 @@ export class Ring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let startIndex = startPos % this.capacity;
|
const startIndex = startPos % this.capacity;
|
||||||
let endIndex = endPos % this.capacity;
|
const endIndex = endPos % this.capacity;
|
||||||
|
|
||||||
// Loop over each channel
|
// Loop over each channel
|
||||||
for (let i = 0; i < dst.length; i += 1) {
|
for (let i = 0; i < dst.length; i += 1) {
|
||||||
|
@ -147,8 +147,8 @@ export class Ring {
|
||||||
|
|
||||||
size() {
|
size() {
|
||||||
// TODO is this thread safe?
|
// TODO is this thread safe?
|
||||||
let readPos = Atomics.load(this.state, STATE.READ_POS)
|
const readPos = Atomics.load(this.state, STATE.READ_POS)
|
||||||
let writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
const writePos = Atomics.load(this.state, STATE.WRITE_POS)
|
||||||
|
|
||||||
return writePos - readPos
|
return writePos - readPos
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class Video {
|
||||||
frame.close()
|
frame.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
play(play: Message.Play) {
|
play(_play: Message.Play) {
|
||||||
// Queue up to render the next frame.
|
// Queue up to render the next frame.
|
||||||
if (!this.render) {
|
if (!this.render) {
|
||||||
this.render = self.requestAnimationFrame(this.draw.bind(this))
|
this.render = self.requestAnimationFrame(this.draw.bind(this))
|
||||||
|
|
|
@ -10,7 +10,7 @@ class Renderer extends AudioWorkletProcessor {
|
||||||
ring?: Ring;
|
ring?: Ring;
|
||||||
base: number;
|
base: number;
|
||||||
|
|
||||||
constructor(params: AudioWorkletNodeOptions) {
|
constructor(_params: AudioWorkletNodeOptions) {
|
||||||
// The super constructor call is required.
|
// The super constructor call is required.
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class Renderer extends AudioWorkletProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inputs and outputs in groups of 128 samples.
|
// Inputs and outputs in groups of 128 samples.
|
||||||
process(inputs: Float32Array[][], outputs: Float32Array[][], parameters: Record<string, Float32Array>): boolean {
|
process(inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array>): boolean {
|
||||||
if (!this.ring) {
|
if (!this.ring) {
|
||||||
// Paused
|
// Paused
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -28,7 +28,7 @@ export default class Reader {
|
||||||
async readAll(): Promise<Uint8Array> {
|
async readAll(): Promise<Uint8Array> {
|
||||||
const r = this.reader.getReader()
|
const r = this.reader.getReader()
|
||||||
|
|
||||||
while (1) {
|
for (;;) {
|
||||||
const result = await r.read()
|
const result = await r.read()
|
||||||
if (result.done) {
|
if (result.done) {
|
||||||
break
|
break
|
||||||
|
@ -164,18 +164,22 @@ export default class Reader {
|
||||||
const size = (first & 0xc0) >> 6
|
const size = (first & 0xc0) >> 6
|
||||||
|
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 0:
|
case 0: {
|
||||||
const v0 = await this.uint8()
|
const v = await this.uint8()
|
||||||
return BigInt(v0) & 0x3fn
|
return BigInt(v) & 0x3fn
|
||||||
case 1:
|
}
|
||||||
const v1 = await this.uint16()
|
case 1: {
|
||||||
return BigInt(v1) & 0x3fffn
|
const v = await this.uint16()
|
||||||
case 2:
|
return BigInt(v) & 0x3fffn
|
||||||
const v2 = await this.uint32()
|
}
|
||||||
return BigInt(v2) & 0x3fffffffn
|
case 2: {
|
||||||
case 3:
|
const v = await this.uint32()
|
||||||
const v3 = await this.uint64()
|
return BigInt(v) & 0x3fffffffn
|
||||||
return v3 & 0x3fffffffffffffffn
|
}
|
||||||
|
case 3: {
|
||||||
|
const v = await this.uint64()
|
||||||
|
return v & 0x3fffffffffffffffn
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw "impossible"
|
throw "impossible"
|
||||||
}
|
}
|
||||||
|
@ -183,7 +187,7 @@ export default class Reader {
|
||||||
|
|
||||||
async done(): Promise<boolean> {
|
async done(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const peek = await this.peek(1)
|
await this.peek(1)
|
||||||
return false
|
return false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return true // Assume EOF
|
return true // Assume EOF
|
||||||
|
|
|
@ -29,7 +29,7 @@ export default class Transport {
|
||||||
|
|
||||||
// Helper function to make creating a promise easier
|
// Helper function to make creating a promise easier
|
||||||
private async connect(config: Config): Promise<WebTransport> {
|
private async connect(config: Config): Promise<WebTransport> {
|
||||||
let options: WebTransportOptions = {};
|
const options: WebTransportOptions = {};
|
||||||
if (config.fingerprint) {
|
if (config.fingerprint) {
|
||||||
options.serverCertificateHashes = [ config.fingerprint ]
|
options.serverCertificateHashes = [ config.fingerprint ]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export interface Init {}
|
// TODO fill in required fields
|
||||||
export interface Segment {}
|
export type Init = any
|
||||||
|
export type Segment = any
|
||||||
|
|
||||||
export interface Debug {
|
export interface Debug {
|
||||||
max_bitrate: number
|
max_bitrate: number
|
||||||
|
|
|
@ -29,7 +29,7 @@ interface WebTransport {
|
||||||
readonly incomingUnidirectionalStreams: ReadableStream;
|
readonly incomingUnidirectionalStreams: ReadableStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare var WebTransport: {
|
declare const WebTransport: {
|
||||||
prototype: WebTransport;
|
prototype: WebTransport;
|
||||||
new(url: string, options?: WebTransportOptions): WebTransport;
|
new(url: string, options?: WebTransportOptions): WebTransport;
|
||||||
};
|
};
|
||||||
|
@ -71,7 +71,7 @@ interface WebTransportError extends DOMException {
|
||||||
readonly streamErrorCode: number;
|
readonly streamErrorCode: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare var WebTransportError: {
|
declare const WebTransportError: {
|
||||||
prototype: WebTransportError;
|
prototype: WebTransportError;
|
||||||
new(init?: WebTransportErrorInit): WebTransportError;
|
new(init?: WebTransportErrorInit): WebTransportError;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,11 +2,11 @@ export default class Deferred<T> {
|
||||||
promise: Promise<T>
|
promise: Promise<T>
|
||||||
resolve: (value: T | PromiseLike<T>) => void
|
resolve: (value: T | PromiseLike<T>) => void
|
||||||
reject: (value: T | PromiseLike<T>) => void
|
reject: (value: T | PromiseLike<T>) => void
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Set initial values so TS stops being annoying.
|
// Set initial values so TS stops being annoying.
|
||||||
this.resolve = (value: T | PromiseLike<T>) => {};
|
this.resolve = (_value: T | PromiseLike<T>) => { /* noop */ };
|
||||||
this.reject = (value: T | PromiseLike<T>) => {};
|
this.reject = (_value: T | PromiseLike<T>) => { /* noop */ };
|
||||||
|
|
||||||
this.promise = new Promise((resolve, reject) => {
|
this.promise = new Promise((resolve, reject) => {
|
||||||
this.resolve = resolve
|
this.resolve = resolve
|
||||||
|
|
1254
web/yarn.lock
1254
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue