@salutejs/jazz-sdk-web
Version:
Jazz SDK for web applications
1,721 lines (1,494 loc) • 226 kB
TypeScript
import { Container, Module, ModuleDeclaration } from 'ditox';
import { Controller } from 'nrgy/mvc';
import { Observable } from 'rxjs';
import { Signal, Atom, Scope } from 'nrgy';
import { Query as Query$2 } from 'rx-effects';
type EventLike$1 = {
type: string;
};
type KeyValueStorageEvent = {
/**
* The origin context of the event can be classified as 'local' when a storage
* is modified by the current window or process, and as 'other' in all other cases.
*/
readonly context: 'local' | 'other';
/**
* Returns a string with the key for the storage item that was changed.
* The key attribute is null when the change is caused by the storage clear() method.
*/
readonly key: string | null;
/**
* Returns a string with the new value of the storage item that was changed.
* This value is null when the change has been invoked by storage clear() method,
* or the storage item has been removed from the storage.
*/
readonly newValue: string | null;
};
/**
* This is a subset of Web Storage API
*/
type KeyValueStorage = Readonly<{
storageEvent: Observable<KeyValueStorageEvent>;
/**
* Returns the current value associated with the given key, or null if the
* given key does not exist in the list associated with the object.
*/
getItem: (key: string) => string | undefined;
/**
* Sets the value of the pair identified by key to value, creating a new
* key/value pair if none existed for key previously.
*
* Throws a "QuotaExceededError" DOMException exception if the new value
* couldn't be set. (Setting could fail if, e.g., the user has disabled
* storage for the site, or if the quota has been exceeded.)
*/
setItem: (key: string, value: string) => void;
/**
* Removes the key/value pair with the given key from the list associated
* with the object, if a key/value pair with the given key exists.
*/
removeItem: (key: string) => void;
/**
* Empties the list associated with the object of all key/value pairs, if
* there are any.
*/
clear: () => void;
getKeys: () => ReadonlyArray<string>;
}>;
type AnyObject = Record<string, any>;
type Timestamp = number;
type UTCDate = string;
/** Autocomplete for string union with ability use any string
* e.g. 'apple' | 'banana' | string
*
* WARNING: doesn't work as object key with empty default value
* following code will throw type error:
* const obj1: Record<Autocomplete<'foo' | 'bar'>, any> = {}
*/
type StringEnum<Enum extends string> = Enum | (string & Record<never, never>);
type ResultFailure<Error> = {
readonly type: 'failure';
readonly error: Error;
};
type ResultSuccess<Value> = {
readonly type: 'success';
readonly value: Value;
};
type SyncResult<Value, Error = unknown> =
| ResultSuccess<Value>
| ResultFailure<Error>;
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
type LogMetadata = Readonly<Record<string, string>>;
type LogEvent = Readonly<{
timestamp: number;
level: LogLevel;
tag: string;
messages: ReadonlyArray<unknown>;
meta?: LogMetadata;
}>;
type BaseRequestQuery = Record<
string,
string | boolean | number | (string | boolean | number)[]
>;
declare class HttpClientResponseError<
ResponseError,
RequestBody,
RequestQuery extends BaseRequestQuery | void,
> extends Error {
type: 'HttpClientResponseError';
readonly request: HttpClientRequest<RequestBody, RequestQuery>;
readonly status: number;
readonly headers: Headers;
readonly ok: false;
readonly redirected: boolean;
readonly statusText: string;
readonly data: ResponseError;
readonly originalResponse: Response;
constructor(
message: string,
response: HttpClientResponseFailure<
ResponseError,
RequestBody,
RequestQuery
>,
replaceResponseData?: ResponseError,
);
}
declare class HttpClientFetchError<
RequestBody = void,
RequestQuery extends BaseRequestQuery | void = void,
> extends Error {
type: 'HttpClientFetchError';
readonly request: HttpClientRequest<RequestBody, RequestQuery>;
constructor(
message: string,
response: HttpClientErrorResponse<RequestBody, RequestQuery>,
);
}
type HttpClientRequest<
RequestBody,
RequestQuery extends BaseRequestQuery | void,
> = {
body: RequestBody;
url: string;
method: string;
credentials: string;
mode: string;
headers: HeadersInit;
query: RequestQuery;
};
type HttpClientResponseFailure<
ResponseError,
RequestBody,
RequestQuery extends BaseRequestQuery | void,
> = {
originalResponse: Response;
status: number;
headers: Headers;
redirected: boolean;
statusText: string;
request: HttpClientRequest<RequestBody, RequestQuery>;
ok: false;
data: ResponseError;
};
type HttpClientErrorResponse<
RequestBody,
RequestQuery extends BaseRequestQuery | void,
> = {
request: HttpClientRequest<RequestBody, RequestQuery>;
};
type ApiAuthErrorResolution = 'retry' | 'fail';
type ConfigFlags<T extends Record<string, unknown>> = Readonly<T>;
type ReactionType = 'applause' | 'surprise' | 'smile' | 'like' | 'dislike';
/**
* @deprecated use type Atom from nrgy
* Provider of a state value.
*/
type Query$1<T> = Query$2<T>;
/**
* Function which unsubscribes from something.
*/
type Unsubscriber$1 = () => void;
/**
* @deprecated use watchAtom with option.skipFirst = false
* Subscribes a listener on a value of the query and its changes.
* @example
* ```ts
import {
handleQuery,
} from '@salutejs/jazz-sdk-web';
...
const unsubscribe = handleQuery(room.participants, (participants) => {
console.log(participants);
});
unsubscribe()
```
*/
declare function handleQuery<T>(
query: Query$1<T> | Atom<T>,
observer: (value: T) => unknown,
): Unsubscriber$1;
/**
* Subscribes a listener on changes of the query only.
* @deprecated use watchAtom
* @example
* ```ts
import {
handleQueryChanges,
} from '@salutejs/jazz-sdk-web';
...
const unsubscribe = handleQueryChanges(room.participants, (participants) => {
console.log(participants);
});
unsubscribe()
```
*/
declare function handleQueryChanges<T>(
query: Query$1<T> | Atom<T>,
listener: (value: T) => unknown,
): Unsubscriber$1;
/**
* Subscribes to changes of the given atom and notifies the listener on new values.
*
* @template T - The type of the values emitted by the atom .
* @param atom - An Atom to observe.
* @param listener - A callback function invoked with the current or updated value.
* @param options - Optional configuration:
* - sync: (not currently used) potential flag to control synchronous behavior.
* - onlyChanges: (not currently used) potential flag to notify only on changed values.
* - skipFirst: Whether to skip the first emitted value (default: true).
*
* @returns A function to unsubscribe from the observed value changes.
*
* This function works by subscribing to the observable stream representing the atom.
* If skipFirst is true, it discards the initial value emission; otherwise, it includes it.
* This allows flexible control over initial notification behavior during subscription.
*
* @example
* ```ts
import {
watchAtom,
} from '@salutejs/jazz-sdk-web';
...
const participants = room.participants()
const unsubscribe = watchAtom(room.participants, (_participants) => {
participants = _participants
});
unsubscribe()
```
*/
declare function watchAtom<T>(
atom: Atom<T> | Query$1<T>,
listener: (value: T) => unknown,
options?: {
sync?: boolean | undefined;
},
): Unsubscriber$1;
/**
* Subscribes a listener on an event bus by type.
*
* @example
* ```ts
import {
handleEvent,
} from '@salutejs/jazz-sdk-web';
...
const unsubscribe = handleEvent(
client.event$,
'addRoom',
async ({ payload }) => {
console.log(payload.room);
},
);
unsubscribe();
```
*/
declare function handleEvent<
Event extends EventLike$1,
Type extends Event['type'],
TypedEvent extends Event & {
type: Type;
},
>(
eventBus: Observable<Event> | Signal<Event>,
type: Type,
listener: (event: TypedEvent) => unknown,
isOnce?: boolean,
): Unsubscriber$1;
/**
* Subscribes a listener on an event bus.
*
* @example
* ```ts
import {
handleEvents,
} from '@salutejs/jazz-sdk-web';
...
const unsubscribe = handleEvents(client.event$, (event) => {
if (event.type === 'addRoom') {
console.log(payload.room);
}
});
unsubscribe()
```
*/
declare function handleEvents<Event extends EventLike$1>(
eventBus: Observable<Event> | Signal<Event>,
listener: (event: Event) => unknown,
isOnce?: boolean,
): Unsubscriber$1;
type PayloadError<Code, Params> = {
code: Code;
reason?: Code;
message?: string;
params?: Params;
};
type RequestId = string;
type EventOk<T = string, P = unknown> = {
event: T;
requestId: RequestId;
roomId: string;
groupId: string | undefined;
payload: P;
};
type EventError<
Code = string,
Params = unknown,
Error extends string = 'error',
> = {
event: Error;
requestId: RequestId;
roomId: string;
groupId: string | undefined;
payload: PayloadError<Code, Params>;
};
/**
* Event name for subscribe to all events in transport
*/
declare type AllEventTypes = '*';
declare type AllNodeTypes =
| TransportRoot<any>
| BaseTransportNode
| BaseTransportNodeReadonly;
declare interface BaseEventBusReadonly<EVENTS extends EventLike>
extends BaseEventBusSubscriber<EVENTS> {
name?: string;
}
declare interface BaseEventBusSubscriber<EVENTS extends EventLike> {
/**
* Method for subscribing to bus events.
* In addition to events of the type, you can also specify the * event,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const eventBus = createBaseEventBus<Events>();
*
* const unsubscriber = eventBus.on('event', (event, payload) => console.log(payload));
* unsubscriber();
*
* eventBus.send('event', 'test');
* ```
*/
on<EVENT extends string & keyof EVENTS>(
event: EVENT,
callback: (event: EVENT, payload: EVENTS[EVENT]) => void,
): Unsubscriber;
/**
* unsubscribe from an event.
* If there are no subscribers left for the event, we remove it from the map.
*
* If the onUnsubscribe lifecycle callback is passed,
* it will be called each time this function is called.
*
* If the transport was destroyed, the method does not work.
*
* @example
* ```ts
* type Events = { event: string };
* const eventBus = createBaseEventBus<Events>();
*
* function handler(type: string, payload: string): void {}
*
* eventBus.on('event', handler);
* eventBus.off('event', handler);
* ```
*/
off<EVENT extends string & keyof EVENTS>(
event: EVENT,
callback: (event: EVENT, payload: EVENTS[EVENT]) => void,
): void;
}
declare interface BaseTransportNode extends DestroyedNode {
name?: string;
__isRoot: Readonly<false>;
/**
* Method to get the root node object referenced by the node.
*/
getTransports: () => TransportRootNodes;
}
declare interface BaseTransportNodeReadonly {
name?: string;
__isRoot: Readonly<false>;
/**
* A property indicating that a class has been destroyed.
* Once resolved, all methods in it stop working and the data is cleared.
*/
isDestroyed: boolean;
/**
* Method to get the root node object referenced by the node.
*/
getTransports: () => TransportRootNodes;
}
declare interface BaseTransportRoot extends DestroyedNode {
name?: string;
__isRoot: Readonly<true>;
}
/**
* A node that has a cleanup mechanism. After the method chchchch is executed,
* the node becomes inactive because event subscriptions and message sending stop functioning.
*/
declare interface DestroyedNode {
/**
* whether the transport is destroyed.
* If the transport is destroyed,
* then subscriptions and event sending do not work, and the subscriber list is destroyed.
* Also, all dependent nodes are automatically unsubscribed from the destroyed node.
*/
isDestroyed: boolean;
destroy(): void;
}
declare type EventLike = Record<string, unknown>;
declare type Namespace = string;
declare interface SubscribeNode<EVENTS extends EventLike>
extends TransportNodeBase<EVENTS> {
add<
TYPES extends string & keyof EVENTS,
TYPE extends Type,
ALL_NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<TYPES, TYPE>,
>(
namespace: ALL_NAMESPACES | '',
root: AllNodeTypes,
): void;
remove<
TYPES extends string & keyof EVENTS,
TYPE extends Type,
ALL_NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<TYPES, TYPE>,
>(
namespace: ALL_NAMESPACES | '',
root: AllNodeTypes,
): void;
channel<
EVENT_TYPES extends string & keyof EVENTS,
TYPES extends string,
CHANNEL extends UtilsTypeFilterTypesWithNamespaces<EVENT_TYPES, TYPES>,
CHANNEL_EVENTS_TYPES extends UtilsTypeRemoveNamespaceFromType<
EVENT_TYPES,
CHANNEL
>,
CHANNEL_EVENTS extends {
[TYPE in CHANNEL_EVENTS_TYPES]: EVENTS[`${CHANNEL}:${TYPE}`];
},
>(
channel: CHANNEL,
): SubscribeNode<CHANNEL_EVENTS>;
asReadonly(): SubscribeReadonlyNode<EVENTS>;
}
declare interface SubscribeNodeSubscribers<EVENTS extends EventLike> {
on<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
NEW_NAMESPACE extends UtilsTypeFilterTypesWithNamespaces<EVENT_TYPE, TYPE>,
CALLBACK_EVENTS extends EVENT_TYPE extends AllEventTypes
? string & EVENTS_KEYS
: EVENT_TYPE extends `${NAMESPACES}:${AllEventTypes}`
? UtilsTypeRemoveNamespaceFromType<string & EVENTS_KEYS, NEW_NAMESPACE>
: EVENT_TYPE,
CALLBACK_PARAMS extends {
[TYPE in CALLBACK_EVENTS]: [event: TYPE, payload: EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CALLBACK_PARAMS[CALLBACK_EVENTS]) => void,
): Unsubscriber;
once<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
NEW_NAMESPACE extends UtilsTypeFilterTypesWithNamespaces<EVENT_TYPE, TYPE>,
CALLBACK_EVENTS extends EVENT_TYPE extends AllEventTypes
? string & EVENTS_KEYS
: EVENT_TYPE extends `${NAMESPACES}:${AllEventTypes}`
? UtilsTypeRemoveNamespaceFromType<string & EVENTS_KEYS, NEW_NAMESPACE>
: EVENT_TYPE,
CALLBACK_PARAMS extends {
[TYPE in CALLBACK_EVENTS]: [event: TYPE, payload: EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CALLBACK_PARAMS[CALLBACK_EVENTS]) => void,
): Unsubscriber;
off<
EVENTS_KEYS extends keyof EVENTS,
TYPE extends string,
NAMESPACES extends UtilsTypeFilterTypesWithNamespaces<
string & EVENTS_KEYS,
TYPE
>,
EVENT_TYPE extends
| `${NAMESPACES}:${AllEventTypes}`
| AllEventTypes
| (string & EVENTS_KEYS),
>(
type: EVENT_TYPE,
callback: (...args: any[]) => void,
): void;
}
declare interface SubscribeReadonlyNode<EVENTS extends EventLike>
extends SubscribeReadonlyNodeExtends<EVENTS> {}
declare type SubscribeReadonlyNodeExtends<EVENTS extends EventLike> =
BaseTransportNodeReadonly & SubscribeNodeSubscribers<EVENTS>;
declare type TransportLifecycleEvents<EVENTS extends EventLike> = {
/**
* The transport was cleared. After that,
* it stops functioning and all data in it is cleared.
*/
destroy: undefined;
/**
* Subscribed to some event.
* The object indicates what event was subscribed to and whether it is the first.
*/
subscribe: {
event: string & keyof EVENTS;
mode: 'on' | 'once';
subscriber: Parameters<TransportRootSubscribers<EVENTS>['on']>[1];
subscribersCount: number;
};
/**
* Unsubscribed from some event.
* The object indicates what event was unsubscribed from and whether there are more subscribers.
*/
unsubscribe: {
event: string & keyof EVENTS;
mode: 'on' | 'once';
subscriber: Parameters<TransportRootSubscribers<EVENTS>['off']>[1];
subscribersCount: number;
};
};
declare type TransportNodeBase<EVENTS extends EventLike> =
SubscribeNodeSubscribers<EVENTS> & BaseTransportNode;
declare interface TransportReadonlyNode<EVENTS extends EventLike>
extends TransportReadonlyNodeBase<EVENTS> {
lifecycle: TransportRoot<EVENTS>['lifecycle'];
}
declare type TransportReadonlyNodeBase<EVENTS extends EventLike> =
TransportRootSubscribers<EVENTS> & BaseTransportNodeReadonly;
declare interface TransportRoot<EVENTS extends EventLike>
extends TransportRootBase<EVENTS> {
/**
* Sync mode sending events
*
* @default false
*/
sync?: Readonly<boolean>;
/**
* Method for sending an event to listeners.
* If the transport was destroyed,
* or no one is subscribed to this event, the method will do nothing.
*
* If there are subscribers to *,
* they will listen to all events that were forwarded.
*
* The method works in 2 modes: synchronous and asynchronous (asynchronous mode is enabled by default).
* To change this, you need to pass the 3rd argument.
*
* @example
* ```ts
* type Events = { event: string, event_empty: undefined };
* const transport = createTransport<Events>();
*
* transport.on('event', (event, payload) => console.log(payload));
* transport.on('event_empty', (event, payload) => console.log(payload));
* transport.on('*', (event, payload) => console.log(payload));
*
* transport.send('event', 'test');
* transport.send('event_empty');
* transport.send('event_empty', undefined);
* ```
*/
send<
TYPE extends string & keyof EVENTS,
PARAMETERS extends EVENTS[TYPE] extends undefined
? (payload?: EVENTS[TYPE]) => void
: (payload: EVENTS[TYPE]) => void,
>(
type: TYPE,
...other: Parameters<PARAMETERS>
): void;
/**
* Method for getting a node that has only subscription interfaces (on/once/off).
* Recommended for use in public API services to hide methods
* for direct control of transport state from the outside.
*/
asReadonly(): TransportReadonlyNode<EVENTS>;
}
declare type TransportRootBase<EVENTS extends EventLike> =
TransportRootSubscribers<EVENTS> &
BaseTransportRoot & {
/**
* Transport lifecycle event bus. You can subscribe to 3 events:
* 1) destroy - the transport was cleared. After that, it stops functioning and all data in it is cleared.
* 2) subscribe - subscribed to some event. The object indicates what event was subscribed to and whether it is the first.
* 3) unsubscribe - unsubscribed from some event. The object indicates what event was unsubscribed from and whether there are more subscribers.
*
* When the main transport is destroyed, the lifecycle event bus also dies.
*
* @example
* ```ts
* const transport = createTransport<Events>();
*
* transport.lifecycle.on('destroy', () => console.log('transport is destroy'));
* transport.lifecycle.on('subscribe', ({ event, isFirstSubscribe }) => console.log(`subscribe to event ${event} isFirst=${isFirstSubscribe}`));
* transport.lifecycle.on('unubscribe', ({ event, isHasSubscribers }) => console.log(`unsubscribe from event ${event} isHasSubscribers=${isHasSubscribers}`));
*
* const unsubscriber1 = transport.on('event1', () => {}) // subscribe to event event1 isFirst=true
* const unsubscriber2 = transport.on('event1', () => {}) // subscribe to event event1 isFirst=false
* const unsubscriber3 = transport.on('event2', () => {}) // subscribe to event event2 isFirst=true
*
* unsubscriber3() // unsubscribe from event event2 isHasSubscribers=false
* unsubscriber2() // unsubscribe from event event1 isHasSubscribers=true
* unsubscriber1() // unsubscribe from event event1 isHasSubscribers=false
*
* transport.destroy(); // transport is destroy
* ```
*/
lifecycle: Readonly<
BaseEventBusReadonly<TransportLifecycleEvents<EVENTS>>
>;
};
/**
* List of nodes the node is subscribed to.
*/
declare type TransportRootNodes = Record<Namespace, Array<TransportRoot<any>>>;
declare interface TransportRootSubscribers<EVENTS extends EventLike> {
/**
* Method for subscribing to bus events.
* In addition to events of the type, you can also specify the * event,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* transport.on('event', (event, payload) => console.log(payload));
* const unsubscriber = transport.on('*', (event, payload) => console.log(payload));
* unsubscriber();
*
* transport.send('event', 'test');
* ```
*/
on<
EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes),
EVENT extends EVENT_TYPE extends AllEventTypes
? string & keyof EVENTS
: EVENT_TYPE,
CB extends {
[TYPE in EVENT]: [TYPE, EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CB[EVENT]) => void,
): Unsubscriber;
/**
* A method for one-time subscription to bus events.
* In addition to events of the type, you can also specify an event *,
* which will allow you to subscribe to all bus events.
* The method returns a function for unsubscribing the callback
* (this can also be done via the off or removeEventListener methods).
*
* If the onSubscribe lifecycle method is passed,
* it will be called when this event is sent.
*
* If the transport was destroyed, this method will do nothing.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* transport.once('event', (event, payload) => console.log(payload));
* const unsubscriber = transport.once('*', (event, payload) => console.log(payload));
* unsubscriber();
*
* transport.send('event', 'test');
* transport.send('event', 'test'); // not call subscribers
* ```
*/
once<
EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes),
EVENT extends EVENT_TYPE extends AllEventTypes
? string & keyof EVENTS
: EVENT_TYPE,
CB extends {
[TYPE in EVENT]: [TYPE, EVENTS[TYPE]];
},
>(
event: EVENT_TYPE,
callback: (...args: CB[EVENT]) => void,
): Unsubscriber;
/**
* unsubscribe from an event.
* If there are no subscribers left for the event, we remove it from the map.
*
* If the onUnsubscribe lifecycle callback is passed,
* it will be called each time this function is called.
*
* If the transport was destroyed, the method does not work.
*
* @example
* ```ts
* type Events = { event: string };
* const transport = createTransport<Events>();
*
* function handler(type: string, payload: string): void {}
*
* transport.on('event', handler);
* transport.off('event', handler);
* ```
*/
off<EVENT_TYPE extends string & (keyof EVENTS | AllEventTypes)>(
event: EVENT_TYPE,
callback: (...args: any[]) => void,
): void;
}
declare type Type = string;
/**
* unsubscribe function to unsubscribe from an event.
*/
declare type Unsubscriber = () => void;
/**
* Utility type for adding to namespace event names
*
* @example
*
* type Event = { event1: boolean; event2: number; }
* UtilsTypeAddNamespaceToEvents<'namespace', Event> // { 'namespace:event1': boolean; 'namespace:event2': number; }
*/
declare type UtilsTypeAddNamespaceToEvents<
NAMESPACE extends string,
EVENT extends EventLike,
> = NAMESPACE extends ''
? EVENT
: {
[TYPE in UtilsTypeAddNamespaceToType<
NAMESPACE,
string & keyof EVENT
>]: EVENT[UtilsTypeRemoveNamespaceFromType<TYPE, NAMESPACE>];
};
/**
* Utility type for gluing namespace and type name
*
* @example
* UtilsTypeAddNamespaceToType<'namespace', 'type'> // 'namespace:type'
*/
declare type UtilsTypeAddNamespaceToType<
NAMESPACE extends string,
TYPE extends string,
> = `${NAMESPACE}:${TYPE}`;
/**
* Utility type for getting namespace from event name (max size 5 namespaces)
*
* @example
*
* UtilsTypeFilterTypesWithNamespaces<'namespace1:event', 'event'> // 'namespace1'
* UtilsTypeFilterTypesWithNamespaces<'namespace1:namespace2:event', 'event'> // 'namespace1:namespace2'
* UtilsTypeFilterTypesWithNamespaces<'namespace1:namespace2:namespace3:event', 'event'> // 'namespace1:namespace2:namespace3'
*/
declare type UtilsTypeFilterTypesWithNamespaces<
STR extends string,
TYPE extends string,
> = STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${infer NAMESPACE_4}:${infer NAMESPACE_5}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}:${NAMESPACE_4}:${NAMESPACE_5}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${infer NAMESPACE_4}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}:${NAMESPACE_4}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${infer NAMESPACE_3}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}:${NAMESPACE_3}`
: STR extends `${infer NAMESPACE_1}:${infer NAMESPACE_2}:${TYPE}`
? `${NAMESPACE_1}:${NAMESPACE_2}`
: STR extends `${infer NAMESPACE}:${TYPE}`
? `${NAMESPACE}`
: never;
/**
* Utility type of extraction from event name type without namespace
*
* @example
* UtilsTypeRemoveNamespaceFromType<'namespace:event', 'namespace'> // 'event'
*/
declare type UtilsTypeRemoveNamespaceFromType<
NAMESPACED_TYPE extends string,
NAMESPACE extends string,
> = NAMESPACED_TYPE extends `${NAMESPACE}:${infer TYPE}` ? TYPE : never;
type JitsiXmppEvents = {
/**
* Indicates error while adding ice candidate.
*/
'xmpp.add_ice_candidate_failed': [];
// Designates an event indicating that the focus has asked us to mute our
// audio.
'xmpp.audio_muted_by_focus': [];
// Designates an event indicating that the focus has asked us to disable our
// camera.
'xmpp.video_muted_by_focus': [];
'xmpp.authentication_required': [];
'xmpp.bridge_down': [];
/**
* Triggered when 'session-accept' is received from the responder.
*/
'xmpp.callaccepted.jingle': [];
// Designates an event indicating that an offer (e.g. Jingle
// session-initiate) was received.
'xmpp.callincoming.jingle': [];
// Triggered when Jicofo kills our media session, this can happen while
// we're still in the MUC, when it decides to terminate the media session.
// For example when the session is idle for too long, because we're the only
// person in the conference room.
'xmpp.callended.jingle': [];
'xmpp.chat_error_received': [];
// The conference properties (as advertised by jicofo) have changed
'xmpp.conference_properties_changed': [];
/**
* This event is triggered when the ICE connects for the first time.
*/
'xmpp.connection.connected': [];
// Designates an event indicating that the connection to the XMPP server
// failed.
'xmpp.connection.failed': [];
// Designates an event indicating that the media (ICE) connection was
// interrupted. This should go to the RTC module.
'xmpp.connection.interrupted': [];
// Designates an event indicating that the media (ICE) connection was
// restored. This should go to the RTC module.
'xmpp.connection.restored': [];
// Designates an event indicating that the media (ICE) connection failed.
// This should go to the RTC module.
'xmpp.connection.ice.failed': [];
// Designates an event indicating that the call has been migrated to a different
// bridge and that the client needs to be restarted for a successful transition.
'xmpp.connection.restart': [];
/**
* Designates an event indicating connection status changes.
*/
'xmpp.connection.status.changed': [];
// Designates an event indicating that the display name of a participant
// has changed.
'xmpp.display_name_changed': [];
/**
* Chat room instance have been added to Strophe.emuc plugin.
*/
'xmpp.emuc_room_added': [];
/**
* Chat room instance have been removed from Strophe.emuc plugin.
*/
'xmpp.emuc_room_removed': [];
'xmpp.etherpad': [];
'xmpp.focus_disconnected': [];
'xmpp.focus_left': [];
'xmpp.graceful_shutdown': [];
/**
* Event fired when 'transport-replace' Jingle message has been received,
* before the new offer is set on the PeerConnection.
*/
'rtc.ice_restarting': [];
/**
* Event fired after the 'transport-replace' message has been processed
* and the new offer has been set successfully.
*/
'rtc.ice_restart_success': [];
/**
* Designates an event indicating that we were kicked from the XMPP MUC.
* @param {boolean} isSelfPresence - whether it is for local participant
* or another participant.
* @param {string} actorJid - the jid of the participant who was initator
* of the kick.
* @param {?string} participantJid - when it is not a kick for local participant,
* this is the jid of the participant which was kicked.
*/
'xmpp.kicked': [];
// Designates an event indicating that our role in the XMPP MUC has changed.
'xmpp.localrole_changed': [];
/**
* Event fired when the unique meeting id is set.
*/
'xmpp.meeting_id_set': [];
// Designates an event indicating that an XMPP message in the MUC was
// received.
'xmpp.message_received': [];
// Designates an event indicating that an invite XMPP message in the MUC was
// received.
'xmpp.invite_message_received': [
roomJid: string,
from: string,
txt: string,
invitePassword: string,
];
// Designates an event indicating that a private XMPP message in the MUC was
// received.
'xmpp.private_message_received': [];
// Designates an event indicating that a bot participant type had changed
'xmpp.muc_member_bot_type_changed': [];
// Designates an event indicating that the XMPP MUC was destroyed.
'xmpp.muc_destroyed': [];
// Designates an event indicating that we have joined the XMPP MUC.
'xmpp.muc_joined': [];
// Designates an event indicating that a participant joined the XMPP MUC.
'xmpp.muc_member_joined': [];
// Designates an event indicating that a participant left the XMPP MUC.
'xmpp.muc_member_left': [];
// Designates an event indicating that a participant joined the lobby XMPP MUC.
'xmpp.muc_lobby_member_joined': [];
// Designates an event indicating that a participant in the lobby XMPP MUC has been updated
'xmpp.muc_lobby_member_updated': [];
// Designates an event indicating that a participant left the XMPP MUC.
'xmpp.muc_lobby_member_left': [];
// Designates an event indicating that a participant was denied access to a conference from the lobby XMPP MUC.
'xmpp.muc_denied access': [];
// Designates an event indicating that local participant left the muc
'xmpp.muc_left': [];
// Designates an event indicating that the MUC role of a participant has
// changed.
'xmpp.muc_role_changed': [];
// Designates an event indicating that the MUC has been locked or unlocked.
'xmpp.muc_lock_changed': [];
// Designates an event indicating that the MUC members only config has changed.
'xmpp.muc_members_only_changed': [];
// Designates an event indicating that a participant in the XMPP MUC has
// advertised that they have audio muted (or unmuted).
'xmpp.audio_muted': [];
// Designates an event indicating that a participant in the XMPP MUC has
// advertised that they have video muted (or unmuted).
'xmpp.video_muted': [];
// Designates an event indicating that the video type (e.g. 'camera' or
// 'screen') for a participant has changed.
// Note: currently this event fires every time we receive presence from
// someone (regardless of whether or not the "video type" changed).
'xmpp.video_type': [];
/**
* Indicates that the features of the participant has been changed.
*/
'xmpp.participant_features_changed': [];
'xmpp.password_required': [];
/**
* Indicates that phone number changed.
*/
'conference.phoneNumberChanged': [];
'xmpp.presence_received': [];
'xmpp.presence_status': [];
'xmpp.prompt_for_login': [];
// xmpp is connected and obtained user media
'xmpp.ready_to_join': [];
/**
* Indicates that recording state changed.
*/
'xmpp.recorderStateChanged': [];
// Designates an event indicating that we received statistics from a
// participant in the MUC.
'xmpp.remote_stats': [];
/**
* Indicates that the offer / answer renegotiation has failed.
*/
'xmpp.renegotiation_failed': [];
'xmpp.room_reservation_error': [];
'xmpp.room_connect_error': [];
'xmpp.room_connect_error.not_allowed': [];
'xmpp.room_join_error': [];
'xmpp.room_connect_error.members_only': [];
/**
* Indicates that max users limit has been reached.
*/
'xmpp.room_max_users_error': [];
// Designates an event indicating that we sent an XMPP message to the MUC.
'xmpp.sending_chat_message': [];
// Designates an event indicating that we sent a private XMPP message to
// a specific user of the muc.
'xmpp.sending_private_chat_message': [];
// Event fired after receiving the confirmation about session accept.
'xmpp.session_accept': [];
// Event fired if we receive an error after sending the session accept.
'xmpp.session_accept_error': [];
/**
* Event fired when we do not get our 'session-accept' acknowledged by
* Jicofo. It most likely means that there is serious problem with our
* connection or XMPP server and we should reload the conference.
*
* We have seen that to happen in BOSH requests race condition when the BOSH
* request table containing the 'session-accept' was discarded by Prosody.
* Jicofo does send the RESULT immediately without any condition, so missing
* packets means that most likely it has never seen our IQ.
*/
'xmpp.session_accept_timeout': [];
/**
* Event fired when speaker stats update message is received.
*/
'xmpp.speaker_stats_received': [];
/**
* Event fired when conference creation timestamp is received.
*/
'xmpp.conference_timestamp_received': [];
/**
* Event fired when we receive a message for AV moderation approved for the local participant.
*/
'xmpp.av_moderation.approved': [];
/**
* Event fired when we receive a message for AV moderation.
*/
'xmpp.av_moderation.received': [];
/**
* Event fired when the moderation enable/disable changes.
*/
'xmpp.av_moderation.changed': [];
/**
* Event fired when we receive message that a new jid was approved.
*/
'xmpp.av_moderation.participant.approved': [];
// Designates an event indicating that we should join the conference with
// audio and/or video muted.
'xmpp.start_muted_from_focus': [];
// Designates an event indicating that the subject of the XMPP MUC has
// changed.
'xmpp.subject_changed': [];
// FIXME: how does it belong to XMPP ? - it's detected by the PeerConnection
// suspending detected
'xmpp.suspend_detected': [];
/**
* Notifies for transcription status changes. The event provides the
* following parameters to its listeners:
*
* @param {String} status - The new status.
*/
'xmpp.transcription_status_changed': [];
/**
* Event fired when 'transport-info' with new ICE candidates is received.
*/
'xmpp.transportinfo.jingle': [];
/**
* Indicates that video SIP GW state changed.
*
* @param {VideoSIPGWConstants} status - Any of the following statuses:
* STATUS_BUSY, STATUS_AVAILABLE or STATUS_UNDEFINED.
*/
'xmpp.videoSIPGWAvailabilityChanged': [];
/**
* Indicates that video SIP GW Session state changed.
* The statuses are any of the following statuses:
* STATE_ON, STATE_OFF, STATE_PENDING, STATE_RETRYING, STATE_FAILED.
* {@see VideoSIPGWConstants}
*
* @param {options} event - {address, oldState, newState, displayName}.
*/
'xmpp.videoSIPGWSessionStateChanged': [];
// Designates an event indicating that the local ICE connection state has
// changed.
'xmpp.ice_connection_state_changed': [];
/**
* Event which is emitted when the body in an XMPP message in the MUC
* contains JSON
*/
'xmmp.json_message_received': [from: string, data: unknown];
};
type JitsiXmppEventType$1 = keyof JitsiXmppEvents;
type JitsiRTCEventType$1 =
/**
* Indicates error while create answer call.
*/
| 'rtc.create_answer_failed'
/**
* Indicates error while create offer call.
*/
| 'rtc.create_offer_failed'
| 'rtc.data_channel_open'
| 'rtc.endpoint_conn_status_changed'
| 'rtc.dominant_speaker_changed'
| 'rtc.lastn_endpoint_changed'
/**
* Event emitted when the user granted/blocked a permission for the camera / mic.
* Used to keep track of the granted permissions on browsers which don't
* support the Permissions API.
*/
| 'rtc.permissions_changed'
| 'rtc.sender_video_constraints_changed'
/**
* Event emitted when {@link RTC.setLastN} method is called to update with
* the new value set.
* The first argument is the value passed to {@link RTC.setLastN}.
*/
| 'rtc.lastn_value_changed'
/**
* Event emitted when ssrc for a local track is extracted and stored
* in {@link TraceablePeerConnection}.
* @param {JitsiLocalTrack} track which ssrc was updated
* @param {string} ssrc that was stored
*/
| 'rtc.local_track_ssrc_updated'
/**
* The max enabled resolution of a local video track was changed.
*/
| 'rtc.local_track_max_enabled_resolution_changed'
| 'rtc.track_attached'
/**
* Event fired when we remote track is added to the conference.
* 1st event argument is the added <tt>JitsiRemoteTrack</tt> instance.
**/
| 'rtc.remote_track_added'
// FIXME get rid of this event in favour of NO_DATA_FROM_SOURCE event
// (currently implemented for local tracks only)
| 'rtc.remote_track_mute'
/**
* Indicates that the remote track has been removed from the conference.
* 1st event argument is the removed {@link JitsiRemoteTrack} instance.
*/
| 'rtc.remote_track_removed'
// FIXME get rid of this event in favour of NO_DATA_FROM_SOURCE event
// (currently implemented for local tracks only)
| 'rtc.remote_track_unmute'
/**
* Indicates error while set local description.
*/
| 'rtc.set_local_description_failed'
/**
* Indicates error while set remote description.
*/
| 'rtc.set_remote_description_failed'
| 'rtc.audio_output_device_changed'
| 'rtc.device_list_changed'
/**
* Indicates that the list with available devices will change.
*/
| 'rtc.device_list_will_change'
| 'rtc.device_list_available'
/**
* Indicates that a message from another participant is received on
* data channel.
*/
| 'rtc.endpoint_message_received'
/**
* Indicates that the remote endpoint stats have been received on data channnel.
*/
| 'rtc.endpoint_stats_received'
/**
* Designates an event indicating that the local ICE username fragment of
* the jingle session has changed.
* The first argument of the vent is <tt>TraceablePeerConnection</tt> which
* is the source of the event.
* The second argument is the actual "ufrag" string.
*/
| 'rtc.local_ufrag_changed'
/**
* Designates an event indicating that the local ICE username fragment of
* the jingle session has changed.
* The first argument of the vent is <tt>TraceablePeerConnection</tt> which
* is the source of the event.
* The second argument is the actual "ufrag" string.
*/
| 'rtc.remote_ufrag_changed'
/**
* Custom server messages
*/
| `rtc.datachannel.${string}`;
type JitsiRTCEventType = JitsiRTCEventType$1;
type JitsiXmppEventType = JitsiXmppEventType$1;
type JitsiOutputType = 'video' | 'audio';
type JitsiVideoType = 'camera' | 'desktop';
// Not typed yet
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type UntypedCallback = (...data: any[]) => void;
type PayloadObject = object;
interface JitsiTrack {
containers: HTMLMediaElement[];
disposed: boolean;
track: MediaStreamTrack;
stream: MediaStream;
videoType?: JitsiVideoType;
ssrc?: number;
// For local tracks
maxEnabledResolution?: number;
isAudioTrack(): boolean;
isWebRTCTrackMuted(): boolean;
isVideoTrack(): boolean;
isLocal(): boolean;
isLocalAudioTrack(): boolean;
isActive(): boolean;
isMuted(): boolean;
getType(): JitsiOutputType;
getOriginalStream(): MediaStream | null;
getStreamId(): string | null;
getTrack(): MediaStreamTrack;
getTrackLabel(): string;
getTrackId(): string | null;
getUsageLabel(): JitsiVideoType | 'mic' | 'default';
getId(): string | null;
getParticipantId(): string | null;
getSourceName(): string | null;
attach(element: HTMLVideoElement | HTMLAudioElement): void;
detach(element: HTMLVideoElement | HTMLAudioElement): void;
dispose(): Promise<void>;
// audioLevel: value between 0 and 1
setAudioLevel(audioLevel: number, tpc: TraceablePeerConnection): void;
getMSID(): string | null;
setAudioOutput(audioOutputDeviceId: string): Promise<void>;
addEventListener(event: string, callback: UntypedCallback): void;
removeEventListener(event: string, callback: UntypedCallback): void;
}
interface JitsiRemoteTrack extends JitsiTrack {
isP2P: boolean;
setMute(value: boolean): void;
getSSRC(): number; // synchronization source identifier
_getStatus(): `readyState: ${MediaStreamTrackState}, muted: ${boolean}, enabled: ${boolean}`;
toString(): `RemoteTrack[userID: ${string}, type: ${JitsiOutputType}, ssrc: ${number}, p2p: ${boolean}, status: ${ReturnType<
JitsiRemoteTrack['_getStatus']
>}]`;
}
type JitsiLocalTrackEffect = Readonly<{
isEnabled: (track: JitsiLocalTrack) => boolean;
isMuted?: () => boolean;
setMuted?: (muted: boolean) => void;
startEffect: (stream: MediaStream) => MediaStream | null;
stopEffect: () => void;
// Jazz custom prop
key?: string;
// Jazz custom prop
order?: 'start' | 'end';
}>;
interface JitsiLocalTrack extends JitsiTrack {
_realDeviceId: string;
deviceId: string;
isEnded(): boolean;
getCameraFacingMode(): 'environment' | 'user' | undefined;
getDeviceId(): string;
setEffect(effect: JitsiLocalTrackEffect | undefined): Promise<void>;
setConference(conference: JitsiConference | null): void;
mute(): Promise<void>;
unmute(): Promise<void>;
stopStream(): void;
toString(): string;
_originalStream: MediaStream | null;
rtcId: number;
conference: JitsiConference | null;
// Jazz custom
addEffect(effect: JitsiLocalTrackEffect): Promise<void>;
// Jazz custom
removeEffect(key?: string): Promose<void>;
// Jazz custom
removeEffects(): Promose<void>;
}
type TPCGroupInfo = {
/** the SSRC groups semantics */
semantics: string;
/** group's SSRCs in order where the first one is group's primary SSRC,
* the second one is secondary (RTX) and so on... */
ssrcs: Array<number>;
};
type TPCSSRCInfo = {
/** an array which holds all track's SSRCs */
ssrcs: Array<number>;
/** an array stores all track's SSRC groups */
groups: Array<TPCGroupInfo>;
};
type TraceablePeerConnection = {
peerconnection?: RTCPeerConnection;
localSSRCs: Map<string, TPCSSRCInfo>;
getLocalSSRC(localTrack: JitsiTrack): number | undefined;
isSimulcastOn(): boolean;
getActiveSimulcastStreams(): number;
getTargetVideoBitrates(): Record<string, number>;
getConfiguredVideoCodec(): string;
configureSenderVideoEncodings(): void;
};
interface JitsiConnectionOptions {
billingId?: string;
hosts: {
domain: string;
muc?: string;
bridge?: string;
focus?: string;
};
bosh?: string;
websocket?: string;
clientNode?: string;
serviceUrl?: string;
confID?: string;
siteID?: string;
startSilent?: boolean;
applicationName?: string;
resolution?: number;
constraints?: {
video: {
aspectRatio: number;
height: {
ideal: number;
max: number;
min: number;
};
width: {
ideal: number;
max: number;
min: number;
};
};
};
enableWebsocketResume?: boolean;
enableNoAudioDetection?: boolean;
enableNoisyMicDetection?: boolean;
enableTalkWhileMuted?: boolean;
enableUnifiedOnChrome?: boolean;
enableLayerSuspension?: boolean;
channelLastN?: number;
disableSimulcast?: boolean;
audioQuality?: {
stereo?: boolean;
opusMaxAverageBitrate?: number; // 6000 - 510000
};
videoQuality?: {
preferredCodec?: string;
enforcePreferredCodec?: boolean;
disabledCodec?: string;
maxBitratesVideo?: {
low: number;
standard: number;
high: number;
};
desktopBitrate?: number;
configureVP9?: boolean; // custom flag for jazz. see patch 28_vp9-encodings.
multiplierVP9?: number; // custom flag for jazz. see patch 28_vp9-encodings.
};
p2p?: {
enabled?: boolean;
disabledCodec?: string;
preferredCodec?: string;
};
enableForcedReload?: boolean;
enableIceRestart?: boolean;
desktopSharingFrameRate?: {
min: number;
max: number;
};
/** Use TURN/UDP servers for the jitsi-videobridge connection */
useTurnUdp?: boolean;
useNewBandwidthAllocationStrategy?: boolean;
websocketKeepAlive?: number;
}
interface JitsiConferenceOptions extends Omit<JitsiConnectionOptions, 'p2p'> {
openBridgeChannel?: string;
startSilent?: boolean;
p2p?: {
enabled?: boolean;
};
}
interface JitsiUser {
getId(): string;
getJid(): string;
getDisplayName(): string | undefined;
getRole(): string;
isAudioMuted(): boolean;
isVideoMuted(): boolean;
getStatus(): string;
getConnectionStatus(): string;
hasAnyVideoTrackWebRTCMuted(): boolean;
isHidden(): boolean;
isReplacing(): boolean;
isReplaced(): boolean;
getBotType(): string | undefined;
getTracks(): Array<JitsiTrack>;
}
interface JitsiCommand {
value?: string;
attributes?: Record<string, string | undefined>;
children?: JitsiCommand[];
tagName?: string;
}
interface DownloadUploadStats {
download: number;
upload: number;
}
interface JitsiBridgeStats {
bitrate?: DownloadUploadStats & {
audio?: DownloadUploadStats;
video?: DownloadUploadStats;
};
connectionQuality?: number;
packetLoss?: {
total: number;
download: number;
upload: number;
};
}
interface JitsiConnectionQuality {
_remoteStats: Record<string, JitsiBridgeStats>;
getStats(): JitsiBridgeStats;
}
interface JitsiStatistics {
addConnectionStatsListener(callback: () => void): void;
removeConnectionStatsListener(callback: () => void): void;
addMediaNotFlowingListener(callback: (track: JitsiRemoteTrack) => void): void;
removeMediaNotFlowingListener(
callback: (track: JitsiRemoteTrack) => void,
): void;
}
type JitsiListenable<EventName> = {
addListener(eventName: EventName, listener: UntypedCallback): () => void;
removeListener(eventName: EventName, listener: UntypedCallback);
// Aliases
addEventListener: JitsiListenable<EventName>['addListener'];
removeEventListener: JitsiListenable<EventName>['removeListener'];
on: JitsiListenable<EventName>['addListener'];
off: JitsiListenable<EventName>['removeListener'];
};
type StropheConnectionHandlerRef = unknown;
type JitsiXmppConnection = {
getJid(): string;
pingDomain: string | undefined | null;
ping: {
ping(
jid: string,
success: () => unknown,
error: () => unknown,
timeout: number,
): void;
};
_stropheConn: {
_doDisconnect(condition: string): void;
_proto: {
_abortAllRequests(): void;
};
addHandler(
handler: (presence: Node) => void,
ns: string | null,
name: string,
type: string | null,
id: string | null,
from?: string,
options?: { matchBare?: boolean },
): StropheConnectionHandlerRef;
deleteHandler(handlerRef: StropheConnectionHandlerRef): void;
};
disconnect(reason?: string): void;
flush(): void;
getXmppLog(): Array<[number, string, string]>;
connection: {
emuc: {
addEventListener(event: string, callback: UntypedCallback): () => void;
removeEventListener(event: string, callback: UntypedCallback): void;
rooms: Record<string, any>;
};
};
};
type JitsiXmppListenable = JitsiListenable<JitsiXmppEventType>;
type JitsiXmpp = JitsiXmppListenable & {
roomjid: string;
myroomjid: string;
connection: JitsiXmppConnection;
members: Record<string, unknown>;
/** @deprecated It doesn't work */
ping(timeout: number): Promise<boolean>;
/** Flush presence state */
sendPresence(): void;
kick(jid: string, reason?: string): void;
lobby?: {
lobbyRoom?: Omit<JitsiXmpp, 'lobby'>;
};
};
type LobbyRoom = {
isSupported: () => boolean;
enable: () => Promise<unknown>;
disable: () => void;
setLobbyRoomJid: (jid: string