@js-toolkit/web-utils 1.59.1 → 1.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EventEmitterListener.utils.d.ts +2 -2
- package/FullscreenController.d.ts +2 -3
- package/FullscreenController.js +1 -1
- package/WakeLockController.d.ts +2 -2
- package/WakeLockController.js +1 -1
- package/fullscreen.js +1 -1
- package/fullscreenUtils.d.ts +1 -0
- package/fullscreenUtils.js +1 -0
- package/getEventAwaiter.d.ts +4 -4
- package/getEventAwaiter.js +1 -1
- package/iframe/getAutoConnector.d.ts +2 -1
- package/iframe/getAutoConnector.js +1 -1
- package/media/PipController.d.ts +1 -1
- package/media/PipController.js +1 -1
- package/media/TextTracksController/TextTracksController.d.ts +1 -1
- package/media/TextTracksController/TextTracksController.js +1 -1
- package/package.json +10 -11
- package/platform/isTelegramWebView.d.ts +4 -0
- package/platform/isTelegramWebView.js +1 -0
- package/serviceWorker/ServiceWorkerInstaller.d.ts +2 -2
- package/serviceWorker/ServiceWorkerInstaller.js +1 -1
- package/webrtc/PeerConnection.d.ts +7 -6
- package/webrtc/PeerConnection.js +1 -1
- package/ws/WSController.d.ts +4 -4
- package/ws/WSController.js +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
2
|
export type DomEventTarget = EventTarget;
|
|
3
3
|
export type EventTargetLike = {
|
|
4
4
|
addEventListener: EventEmitterLike['on'];
|
|
@@ -23,7 +23,7 @@ export type GetEventListener<T extends EmitterTarget, E, EM extends AnyObject =
|
|
|
23
23
|
type ListenersMapToEventMap<T extends Record<string, AnyFunction[]>> = {
|
|
24
24
|
[P in keyof T]: Parameters<T[P][number]>[0];
|
|
25
25
|
};
|
|
26
|
-
export type GetEventMap<T> = T extends Document ? DocumentEventMap : T extends HTMLBodyElement ? HTMLBodyElementEventMap : T extends HTMLVideoElement ? HTMLVideoElementEventMap : T extends HTMLMediaElement ? HTMLMediaElementEventMap : T extends TextTrackList ? TextTrackListEventMap : T extends HTMLElement ? HTMLElementEventMap : T extends Element ? ElementEventMap : T extends Animation ? AnimationEventMap : T extends AbortSignal ? AbortSignalEventMap : T extends BroadcastChannel ? BroadcastChannelEventMap : T extends WebSocket ? WebSocketEventMap : T extends MediaStreamTrack ? MediaStreamTrackEventMap : T extends MediaStream ? MediaStreamEventMap : T extends EventSource ? EventSourceEventMap : T extends SourceBuffer ? SourceBufferEventMap : T extends SourceBufferList ? SourceBufferListEventMap : T extends
|
|
26
|
+
export type GetEventMap<T> = T extends Document ? DocumentEventMap : T extends HTMLBodyElement ? HTMLBodyElementEventMap : T extends HTMLVideoElement ? HTMLVideoElementEventMap : T extends HTMLMediaElement ? HTMLMediaElementEventMap : T extends TextTrackList ? TextTrackListEventMap : T extends HTMLElement ? HTMLElementEventMap : T extends Element ? ElementEventMap : T extends Animation ? AnimationEventMap : T extends AbortSignal ? AbortSignalEventMap : T extends BroadcastChannel ? BroadcastChannelEventMap : T extends WebSocket ? WebSocketEventMap : T extends MediaStreamTrack ? MediaStreamTrackEventMap : T extends MediaStream ? MediaStreamEventMap : T extends EventSource ? EventSourceEventMap : T extends SourceBuffer ? SourceBufferEventMap : T extends SourceBufferList ? SourceBufferListEventMap : T extends EventEmitter<any, any> ? ListenersMapToEventMap<ReturnType<T['getEventListeners']>> : EmptyObject;
|
|
27
27
|
export declare function isEventTargetLike(target: EmitterTarget): target is EventTargetLike;
|
|
28
28
|
export declare function isDomEventTarget(target: EmitterTarget): target is DomEventTarget;
|
|
29
29
|
export declare function isEventEmitterLike(target: EmitterTarget): target is EventEmitterLike;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventEmitter } from '
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
2
|
declare global {
|
|
3
3
|
interface HTMLMediaElement {
|
|
4
4
|
webkitEnterFullscreen?: VoidFunction | undefined;
|
|
@@ -7,7 +7,6 @@ declare global {
|
|
|
7
7
|
webkitSupportsFullscreen?: boolean | undefined;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
-
export declare function enterPseudoFullscreen(element: Element & ElementCSSInlineStyle): VoidFunction;
|
|
11
10
|
export declare class FullscreenController extends EventEmitter<FullscreenController.EventMap> implements AsyncDisposable {
|
|
12
11
|
private readonly element;
|
|
13
12
|
private options;
|
|
@@ -55,7 +54,7 @@ export declare namespace FullscreenController {
|
|
|
55
54
|
type: FullscreenType;
|
|
56
55
|
}];
|
|
57
56
|
}>;
|
|
58
|
-
type EventHandler<T extends Events = Events> = EventEmitter.
|
|
57
|
+
type EventHandler<T extends Events = Events> = EventEmitter.DataEventListener<EventMap, T, FullscreenController>;
|
|
59
58
|
type EventHandlerMap<T extends Events = Events> = {
|
|
60
59
|
[P in T]: EventHandler<P>;
|
|
61
60
|
};
|
package/FullscreenController.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter}from"
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{hasIn}from"@js-toolkit/utils/hasIn";import{toggleNativeSubtitles}from"./media/toggleNativeSubtitles";import{fullscreen}from"./fullscreen";import{enterPseudoFullscreen}from"./fullscreenUtils";export class FullscreenController extends EventEmitter{element;options;static isApiAvailable(){return fullscreen.isApiEnabled()}get Events(){return FullscreenController.Events}fallback;exitPseudoFullscreen;constructor(e,t={}){super(),this.element=e,this.options=t,this.setOptions(t)}unbind(){if(fullscreen.names){const{names:e}=fullscreen;this.element.removeEventListener(e.changeEventName,this.nativeChangeHandler),this.element.removeEventListener(e.errorEventName,this.nativeErrorHandler)}this.fallback instanceof HTMLVideoElement&&(this.fallback.removeEventListener("webkitbeginfullscreen",this.videoBeginFullscreenHandler),this.fallback.removeEventListener("webkitendfullscreen",this.videoEndFullscreenHandler),this.fallback=void 0)}setOptions(e){if(this.unbind(),this.options=e??{},this.fallback=this.options.fallback,fullscreen.names&&fullscreen.isApiEnabled()){const{names:e}=fullscreen;this.element.addEventListener(e.changeEventName,this.nativeChangeHandler),this.element.addEventListener(e.errorEventName,this.nativeErrorHandler)}else this.fallback instanceof HTMLVideoElement&&(this.fallback.addEventListener("webkitbeginfullscreen",this.videoBeginFullscreenHandler),this.fallback.addEventListener("webkitendfullscreen",this.videoEndFullscreenHandler))}isAvailable(){return fullscreen.isApiEnabled()||"pseudo"===this.fallback||this.fallback instanceof HTMLVideoElement&&!!this.fallback.webkitEnterFullscreen&&!!this.fallback.webkitSupportsFullscreen}isFullscreen(){return!!this.getCurrentElement()}isPseudoFullscreen(){return this.isFullscreen()&&!!this.exitPseudoFullscreen}getCurrentElement(){if(fullscreen.isApiEnabled()){if(fullscreen.getElement()===this.element)return this.element}else{if(this.exitPseudoFullscreen)return this.element;if(this.fallback instanceof HTMLVideoElement&&this.fallback.webkitDisplayingFullscreen)return this.fallback}return null}nativeChangeHandler=()=>{this.emit(this.Events.Change,{fullscreen:this.isFullscreen(),type:"native"})};nativeErrorHandler=e=>{this.emit(this.Events.Error,{error:e,type:"native"})};videoBeginFullscreenHandler=(()=>{const e=()=>{const t=this.fallback;e.controls=t.controls,e.nativeSubtitles=hasIn(this.options,"toggleNativeSubtitles")&&this.options.toggleNativeSubtitles&&t.textTracks.length>0,e.nativeSubtitles&&toggleNativeSubtitles(!0,t.textTracks),this.emit(this.Events.Change,{fullscreen:!0,type:"video"})};return e.nativeSubtitles=void 0,e.controls=void 0,e})();videoEndFullscreenHandler=()=>{const e=this.fallback;null!=this.videoBeginFullscreenHandler.controls&&(e.controls=this.videoBeginFullscreenHandler.controls),this.videoBeginFullscreenHandler.nativeSubtitles&&toggleNativeSubtitles(!1,e.textTracks),this.emit(this.Events.Change,{fullscreen:!1,type:"video"})};request(e={}){return new Promise(((t,n)=>{if(this.isFullscreen())return void t();if(fullscreen.isApiEnabled())return void fullscreen.request(this.element,e).then(t).catch(n);if("pseudo"===this.fallback)return this.exitPseudoFullscreen=enterPseudoFullscreen(this.element),this.emit(this.Events.Change,{fullscreen:!0,type:"pseudo"}),void t();const l=this.fallback instanceof HTMLVideoElement&&this.fallback||void 0;if(l?.webkitEnterFullscreen&&l.webkitSupportsFullscreen){const e=()=>{l.removeEventListener("webkitbeginfullscreen",e),t()};return l.addEventListener("webkitbeginfullscreen",e),void l.webkitEnterFullscreen()}n(new fullscreen.UnavailableError)}))}exit(){return new Promise(((e,t)=>{if(!this.isFullscreen())return void e();if(fullscreen.isApiEnabled())return void fullscreen.exit().then(e).catch(t);if(this.exitPseudoFullscreen)return this.exitPseudoFullscreen(),this.exitPseudoFullscreen=void 0,this.emit(this.Events.Change,{fullscreen:!1,type:"pseudo"}),void e();const n=this.fallback instanceof HTMLVideoElement&&this.fallback||void 0;if(n?.webkitExitFullscreen&&n.webkitSupportsFullscreen){const t=()=>{n.removeEventListener("webkitendfullscreen",t),e()};return n.addEventListener("webkitendfullscreen",t),void n.webkitExitFullscreen()}t(new fullscreen.UnavailableError)}))}destroy(){return this.exit().then((()=>new Promise((e=>{requestAnimationFrame((()=>{this.removeAllListeners(),this.unbind(),e()}))}))))}[Symbol.asyncDispose](){return this.destroy()}}!function(e){let t;!function(e){e.Change="change",e.Error="error"}(t=e.Events||(e.Events={}))}(FullscreenController||(FullscreenController={}));
|
package/WakeLockController.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class WakeLockController extends
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
|
+
export declare class WakeLockController extends EventEmitter<{
|
|
3
3
|
activated: [];
|
|
4
4
|
deactivated: [];
|
|
5
5
|
error: [{
|
package/WakeLockController.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{hasIn}from"@js-toolkit/utils/hasIn";export class WakeLockController extends EventEmitter{static isApiAvailable(){return hasIn(navigator,"wakeLock")&&null!=navigator.wakeLock}wakelock;releasing=!1;relockOnVisible=!1;restoreWakeLock=()=>{"visible"===document.visibilityState&&this.relockOnVisible&&(this.relockOnVisible=!1,this.request().catch((e=>this.emit("error",{error:e}))))};onRelease=()=>{this.wakelock&&(this.wakelock.removeEventListener("release",this.onRelease),this.wakelock=void 0,this.releasing?document.removeEventListener("visibilitychange",this.restoreWakeLock):"hidden"===document.visibilityState&&(this.relockOnVisible=!0,document.addEventListener("visibilitychange",this.restoreWakeLock)),this.emit("deactivated"))};isActive(){return!!this.wakelock&&!this.wakelock.released}async request(){if(!this.wakelock){try{this.wakelock=await navigator.wakeLock.request("screen")}catch(e){if("visible"===document.visibilityState)throw e;return this.relockOnVisible=!0,document.addEventListener("visibilitychange",this.restoreWakeLock),void this.emit("error",{error:e})}this.relockOnVisible=!1,this.wakelock.addEventListener("release",this.onRelease),this.emit("activated")}}async release(){if(this.wakelock&&!this.wakelock.released)try{this.releasing=!0,await this.wakelock.release(),this.relockOnVisible=!1}finally{this.releasing=!1}}async destroy(){await this.release(),this.removeAllListeners()}[Symbol.asyncDispose](){return this.destroy()}}
|
package/fullscreen.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{es5ErrorCompat}from"@js-toolkit/utils/es5ErrorCompat";import{promisify}from"@js-toolkit/utils/promisify";export class FullscreenUnavailableError extends Error{constructor(){super("Fullscreen is not available"),es5ErrorCompat(this,FullscreenUnavailableError)}}export var fullscreen;!function(e){e.names=[{requestFullscreenName:"requestFullscreen",exitFullscreenName:"exitFullscreen",fullscreenElementName:"fullscreenElement",fullscreenEnabledName:"fullscreenEnabled",changeEventName:"fullscreenchange",errorEventName:"fullscreenerror"},{requestFullscreenName:"webkitRequestFullscreen",exitFullscreenName:"webkitExitFullscreen",fullscreenElementName:"webkitFullscreenElement",fullscreenEnabledName:"webkitFullscreenEnabled",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"webkitRequestFullScreen",exitFullscreenName:"webkitCancelFullScreen",fullscreenElementName:"webkitCurrentFullScreenElement",fullscreenEnabledName:"webkitCancelFullScreen",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"mozRequestFullScreen",exitFullscreenName:"mozCancelFullScreen",fullscreenElementName:"mozFullScreenElement",fullscreenEnabledName:"mozFullScreenEnabled",changeEventName:"mozfullscreenchange",errorEventName:"mozfullscreenerror"},{requestFullscreenName:"msRequestFullscreen",exitFullscreenName:"msExitFullscreen",fullscreenElementName:"msFullscreenElement",fullscreenEnabledName:"msFullscreenEnabled",changeEventName:"MSFullscreenChange",errorEventName:"MSFullscreenError"}].find((({exitFullscreenName:e})=>e in document));const n={change:e.names?.changeEventName,error:e.names?.errorEventName};function r(){if(!e.names)throw new e.UnavailableError;return Boolean(document[e.names.fullscreenElementName])}function l(e,r,l){const
|
|
1
|
+
import{es5ErrorCompat}from"@js-toolkit/utils/es5ErrorCompat";import{promisify}from"@js-toolkit/utils/promisify";export class FullscreenUnavailableError extends Error{constructor(){super("Fullscreen is not available"),es5ErrorCompat(this,FullscreenUnavailableError)}}export var fullscreen;!function(e){e.names=[{requestFullscreenName:"requestFullscreen",exitFullscreenName:"exitFullscreen",fullscreenElementName:"fullscreenElement",fullscreenEnabledName:"fullscreenEnabled",changeEventName:"fullscreenchange",errorEventName:"fullscreenerror"},{requestFullscreenName:"webkitRequestFullscreen",exitFullscreenName:"webkitExitFullscreen",fullscreenElementName:"webkitFullscreenElement",fullscreenEnabledName:"webkitFullscreenEnabled",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"webkitRequestFullScreen",exitFullscreenName:"webkitCancelFullScreen",fullscreenElementName:"webkitCurrentFullScreenElement",fullscreenEnabledName:"webkitCancelFullScreen",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"mozRequestFullScreen",exitFullscreenName:"mozCancelFullScreen",fullscreenElementName:"mozFullScreenElement",fullscreenEnabledName:"mozFullScreenEnabled",changeEventName:"mozfullscreenchange",errorEventName:"mozfullscreenerror"},{requestFullscreenName:"msRequestFullscreen",exitFullscreenName:"msExitFullscreen",fullscreenElementName:"msFullscreenElement",fullscreenEnabledName:"msFullscreenEnabled",changeEventName:"MSFullscreenChange",errorEventName:"MSFullscreenError"}].find((({exitFullscreenName:e})=>e in document));const n={change:e.names?.changeEventName,error:e.names?.errorEventName};function r(){if(!e.names)throw new e.UnavailableError;return Boolean(document[e.names.fullscreenElementName])}function l(e,r,l){const a=n[e];a&&document.addEventListener(a,r,l)}function a(e,r,l){const a=n[e];a&&document.removeEventListener(a,r,l)}function t(n,r){return new Promise(((t,s)=>{if(!e.names)throw new e.UnavailableError;const c=()=>{a("change",c),a("error",u),t()},u=e=>{a("change",c),a("error",u),s(e)};l("change",c),l("error",u),n[e.names.requestFullscreenName](r)}))}function s(){return new Promise(((n,t)=>{if(!e.names)throw new e.UnavailableError;if(!r())return void n();const s=()=>{a("change",s),a("error",c),n()},c=e=>{a("change",s),a("error",c),t(e)};l("change",s),l("error",c),document[e.names.exitFullscreenName]()}))}e.UnavailableError=FullscreenUnavailableError,e.isApiAvailable=function(){return!!e.names},e.isApiEnabled=function(){return!!e.names&&Boolean(document[e.names.fullscreenEnabledName])},e.isFullscreen=r,e.getElement=function(){if(!e.names)throw new e.UnavailableError;return document[e.names.fullscreenElementName]},e.on=l,e.off=a,e.request=t,e.exit=s,e.toggle=function(e){return promisify((()=>r()?s():t(e)))},e.onChange=function(e){l("change",e)},e.onError=function(e){l("error",e)}}(fullscreen||(fullscreen={}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function enterPseudoFullscreen(element: Element & ElementCSSInlineStyle): VoidFunction;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function enterPseudoFullscreen(t){let e,i;return e={position:t.style.position,left:t.style.left,top:t.style.top,width:t.style.width,height:t.style.height,maxWidth:t.style.maxWidth,maxHeight:t.style.maxHeight,zIndex:t.style.zIndex},i=t,i.style.position="fixed",i.style.left="0px",i.style.top="0px",i.style.width="100%",i.style.height="100%",i.style.maxWidth="100%",i.style.maxHeight="100%",i.style.zIndex="99999",()=>{e&&i&&(i.style.position=e.position,i.style.left=e.left,i.style.top=e.top,i.style.width=e.width,i.style.height=e.height,i.style.maxWidth=e.maxWidth,i.style.maxHeight=e.maxHeight,i.style.zIndex=e.zIndex),e=void 0,i=void 0}}
|
package/getEventAwaiter.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type Awaiter } from '@js-toolkit/utils/getAwaiter';
|
|
1
|
+
import { type Awaiter, type AwaiterOptions } from '@js-toolkit/utils/getAwaiter';
|
|
2
2
|
import { type EmitterTarget, type EventTargetLike, type GetEventType, type GetEventTypeFromFn } from './EventEmitterListener.utils';
|
|
3
|
-
export type EventAwaiter = Awaiter<
|
|
4
|
-
interface Options {
|
|
3
|
+
export type EventAwaiter = Awaiter<any>;
|
|
4
|
+
interface Options extends AwaiterOptions {
|
|
5
5
|
/** If it returns true then resolve is ignored. */
|
|
6
6
|
readonly ignoreResolve?: (event: any) => boolean;
|
|
7
7
|
/** If it returns null or undefined then reject is ignored. */
|
|
8
8
|
readonly eventToError?: (event: any) => unknown;
|
|
9
9
|
}
|
|
10
|
-
export declare function getEventAwaiter<T extends EmitterTarget, E extends T extends EventTargetLike ? GetEventTypeFromFn<OverloadToUnion<T['addEventListener']>> : GetEventType<T>>(target: T, resolveEvent: E | E[], rejectEvent?: E | E[], { ignoreResolve, eventToError }?: Options): EventAwaiter;
|
|
10
|
+
export declare function getEventAwaiter<T extends EmitterTarget, E extends T extends EventTargetLike ? GetEventTypeFromFn<OverloadToUnion<T['addEventListener']>> : GetEventType<T>>(target: T, resolveEvent: E | E[], rejectEvent?: E | E[], { ignoreResolve, eventToError, lazy, ...rest }?: Options): EventAwaiter;
|
|
11
11
|
export {};
|
package/getEventAwaiter.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getAwaiter}from"@js-toolkit/utils/getAwaiter";import{EventEmitterListener}from"./EventEmitterListener";import{isEventEmitterLike}from"./EventEmitterListener.utils";export function getEventAwaiter(e,t,r,{ignoreResolve:i,eventToError:o}={}){const
|
|
1
|
+
import{getAwaiter}from"@js-toolkit/utils/getAwaiter";import{EventEmitterListener}from"./EventEmitterListener";import{isEventEmitterLike}from"./EventEmitterListener.utils";export function getEventAwaiter(e,t,r,{ignoreResolve:i,eventToError:o,lazy:n=!0,...s}={}){const E=getAwaiter({lazy:n,...s}),a=Array.isArray(t)?t:[t],f=Array.isArray(r)?r:[r],m=isEventEmitterLike(e)?e:new EventEmitterListener(e),v=e=>{!(!!i&&i(e))&&E.resolve(e)},c=e=>{const t=o?o(e):e;null!=t&&E.reject(t)},l=()=>{a.forEach((e=>m.off(e,v))),f.forEach((e=>m.off(e,c)))},A=E.resolve;E.resolve=(...e)=>(l(),A(...e));const y=E.reject;return E.reject=(...e)=>{y(...e),l()},a.forEach((e=>m.on(e,v))),f.forEach((e=>m.on(e,c))),E}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import log from '@js-toolkit/utils/log';
|
|
1
2
|
import { type MessagesTypes } from './messages';
|
|
2
3
|
import { type Target } from './utils';
|
|
3
4
|
export { getClientMessages, getHostMessages } from './messages';
|
|
@@ -27,7 +28,7 @@ export type AutoConnectorOptions<SendData = AnyObject, ReceiveData = unknown> =
|
|
|
27
28
|
readonly messagesTypes: MessagesTypes;
|
|
28
29
|
/** Process messages only from targets passed to `start` method. Default `true`. */
|
|
29
30
|
readonly strictTargets?: boolean;
|
|
30
|
-
readonly logger?: Pick<
|
|
31
|
+
readonly logger?: Pick<log.Logger, 'v1' | 'warn'> | undefined;
|
|
31
32
|
} & ({
|
|
32
33
|
readonly channel: 'open' | 'use';
|
|
33
34
|
readonly onConnect: (info: ConnectTargetInfo<ReceiveData>, port: MessagePort) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{v4 as uuid}from"uuid";import{onDOMReady}from"../onDOMReady";import{isPingMessage,isTargetReadyMessage}from"./messages";import{isWindowProxy,readTargets}from"./utils";import{getOriginFromMessage}from"./getOriginFromMessage";export{getClientMessages,getHostMessages}from"./messages";export function getAutoConnector({id:e,label:t,strictTargets:
|
|
1
|
+
import{v4 as uuid}from"uuid";import log from"@js-toolkit/utils/log";import{onDOMReady}from"../onDOMReady";import{isPingMessage,isTargetReadyMessage}from"./messages";import{isWindowProxy,readTargets}from"./utils";import{getOriginFromMessage}from"./getOriginFromMessage";export{getClientMessages,getHostMessages}from"./messages";export function getAutoConnector({id:e,label:t,strictTargets:o=!0,messagesTypes:s,channel:n,logger:i=log.getLogger("AutoConnector"),onSendData:r,onConnect:a}){const d=e||uuid(),g=t||d,c=new Map,f="open"===n?new Map:void 0;let u,l;const p=(e,t,o,s,n)=>{if(window!==t)if(isWindowProxy(t)){t.postMessage(e,o,n);const r=t===window.parent?"iframe parent":"iframe";i.v1(`${g}: Post message to ${r} (uid=${s},self.uid=${d},origin=${o}):`,e)}else t.postMessage(e,n&&{transfer:n}),i.v1(`${g}: Post message to MessageEventSource (uid=${s},self.uid=${d}):`,e)},m=(e,t,o)=>{p({uid:d,type:s.Ping},e,t,o)},h=e=>{if(!e.source||e.source===window)return;if(!isPingMessage(e.data,s)&&!isTargetReadyMessage(e.data,s))return;if(e.data.uid===d)return;const t=e.source,l=e.data.uid;if(i.v1(`${g}: Receive message from iframe (uid=${l},self.uid=${d},origin=${e.origin}):`,e.data),o&&u&&!u.has(t))return void i.v1(`${g}: Could not find target (uid=${l},self.uid=${d}) by message.source.`);const h=getOriginFromMessage(e);if(isPingMessage(e.data,s)&&!c.get(l)?.Ping)return c.set(l,{...c.get(l),Ping:!0}),void m(t,h,l);let w=!1;if(c.get(l)?.Ping&&!c.get(l)?.SelfReady){c.set(l,{...c.get(l),SelfReady:!0});const e={target:t,origin:h},o=f?(()=>{const t=f.get(l)?.[0]??new MessageChannel;return f.set(l,[t,e]),t.port2})():void 0;((e,t,o,n,i)=>{p({uid:d,type:s.SelfReady,data:e},t,o,n,i?[i]:void 0)})(r?r(e):void 0,t,h,l,o),w=!1}if(isTargetReadyMessage(e.data,s)&&c.get(l)?.SelfReady){c.delete(l),u?.delete(t);const o=(()=>{if("open"===n){const e=f?.get(l)?.[0].port1;if(!e)throw new Error("Something went wrong: MessageChannel is not created despite the fact that the `channel` option is `open`.");return e}if("use"===n){const t=e.ports[0];if(!t)throw new Error("MessagePort is not received despite the fact that the `channel` option is `use`. The `channel` option of connector on another side must be equals `open`.");return t}})(),{data:s}=e.data,r=()=>{i.v1(`${g}: Connection established (self.uid=${d} + uid=${l}).`),a({data:s,target:t,origin:h},o)};w?setTimeout(r,0):r()}},w=()=>{l&&(l(),l=void 0)};return{start:(e,t={})=>{if(l&&!t.append)return void i.warn(`${g}: Already started. You should first call \`stop\`.`);if(t.append){const e=u;w(),u=e}const o=()=>{const o=(()=>{const t="function"==typeof e?e():e;return t.length>0?t:void 0})(),s=o&&readTargets(o);if(!s)return;const n=new Set(s);t.append?(u||(u=new Set),n.forEach((e=>u.add(e)))):u=n,window.addEventListener("message",h);(t.append?n:u).forEach((e=>{e!==window&&m(e,"*","")}))},s="function"==typeof e?onDOMReady(o):o();l=()=>{s&&s(),window.removeEventListener("message",h),u=void 0}},stop:w,isStarted:()=>!!l,close:e=>{const t=e instanceof Set?e:new Set(readTargets(e));if(0!==t.size&&(f&&f.forEach((([e,o],s)=>{t.has(o.target)&&(e.port1.close(),e.port2.close(),f.delete(s))})),u)){const e=u;t.forEach((t=>e.delete(t)))}},destroy:()=>{w(),c.clear(),f&&(f.forEach((([e])=>{e.port1.close(),e.port2.close()})),f.clear())},[Symbol.dispose](){this.destroy()}}}
|
package/media/PipController.d.ts
CHANGED
package/media/PipController.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter}from"
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventEmitterListener}from"../EventEmitterListener";const getPipUnavailableError=()=>new Error("PiP is not available");export class PipController extends EventEmitter{static isApiEnabled(){return!!HTMLVideoElement.prototype.requestPictureInPicture&&!!document.exitPictureInPicture&&!!document.pictureInPictureEnabled}static isWebkitApiEnabled(e){return!!e.webkitSupportsPresentationMode&&e.webkitSupportsPresentationMode("picture-in-picture")}static isAvailable(e){return this.isApiEnabled()&&!e.disablePictureInPicture||this.isWebkitApiEnabled(e)}get Events(){return PipController.Events}listener;constructor(e){if(super(),this.listener=new EventEmitterListener(e),PipController.isAvailable(e)){const e=()=>{this.emit(this.Events.Change,{pip:!0})},t=()=>{this.emit(this.Events.Change,{pip:!1})};PipController.isApiEnabled()?(this.listener.on("enterpictureinpicture",e),this.listener.on("leavepictureinpicture",t)):this.listener.on("webkitpresentationmodechanged",(()=>{let i=this.listener.target.webkitPresentationMode;return()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():"picture-in-picture"===i&&t(),i=this.listener.target.webkitPresentationMode}})(),!0)}}isPip(){return PipController.isApiEnabled()?document.pictureInPictureElement===this.listener.target:"picture-in-picture"===this.listener.target.webkitPresentationMode}getCurrentElement(){return this.isPip()?this.listener.target:null}request(){return new Promise(((e,t)=>{if(this.isPip())e();else{if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?this.listener.target.requestPictureInPicture().then((()=>e()),t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("picture-in-picture"))}}))}exit(){return new Promise(((e,t)=>{if(this.isPip()){if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?document.exitPictureInPicture().then(e,t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"!==this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("inline"))}else e()}))}destroy(){return this.exit().finally((()=>{this.removeAllListeners(),this.listener.removeAllListeners()}))}[Symbol.asyncDispose](){return this.destroy()}}!function(e){let t;!function(e){e.Change="change"}(t=e.Events||(e.Events={}))}(PipController||(PipController={}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EventEmitter } from '
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
2
|
import { type ActivateTextTrackInfo, type TextTrackInfo, type TextTrackItem } from './utils';
|
|
3
3
|
declare global {
|
|
4
4
|
interface HTMLMediaElementEventMap extends TextTracksEventMap {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter}from"
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventListeners}from"../../EventListeners";import{MediaNotAttachedError}from"../MediaNotAttachedError";import{parseTextTracks,setActiveTextTrack,addTextTracks,isIOSFullscreen,splitRows,buildCueId}from"./utils";function dispatchNativeEvent(t,e,s){t.dispatchEvent(new CustomEvent(e,{detail:s}))}export class TextTracksController extends EventEmitter{options;eventListeners=new EventListeners;addedTracks=[];textTrackList=[];media;textTrack;nextTextTrack;get Events(){return TextTracksController.Events}constructor(t){super(),this.options={emitNativeEvents:!1,hideActiveTrack:!0,preferCueRowLength:0},t&&this.setOptions(t)}setOptions(t){Object.assign(this.options,{...t,emitNativeEvents:t.emitNativeEvents??this.options.emitNativeEvents,hideActiveTrack:t.hideActiveTrack??this.options.hideActiveTrack,preferCueRowLength:t.preferCueRowLength??this.options.preferCueRowLength})}isAttached(){return!!this.media}getMediaElement(){if(!this.media)throw new MediaNotAttachedError;return this.media}detach(){this.eventListeners.removeAllListeners(),this.media=void 0,this.addedTracks=[],this.textTrackList=[],this.textTrack=void 0,this.nextTextTrack=void 0}attach(t){this.detach();const e=(()=>{const e=[];return s=>{const{activeCues:i}=s.target;if(!i)return;let a=e.length!==i.length;const r=new Array(i.length);for(let t=0;t<r.length;t+=1){const s=i[t];s.id=s.id||buildCueId(s,t),r[t]=s,r[t].rows=splitRows(s.text,this.options.preferCueRowLength),a||e[t]?.id===s.id||(a=!0),e[t]=s}e.length>r.length&&e.splice(r.length-e.length),a&&(this.emit(this.Events.TextTrackCueChanged,{textTrack:s.target,cues:r}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackcuechange",{textTrack:s.target,cues:r}))}})(),s=()=>{this.textTrackList=parseTextTracks(t),this.emit(this.Events.TextTrackListChanged,{textTracks:this.textTrackList}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttracklistchange",{textTracks:this.textTrackList}),setActiveTextTrack(t,this.textTrack??this.nextTextTrack,this.options.hideActiveTrack&&!isIOSFullscreen(t))};this.media=t;let i=!1;const a=t=>{i||s(),this.eventListeners.scope(t).on("cuechange",e)},r=t=>{s(),this.eventListeners.scope(t).off("cuechange",e)};if(this.eventListeners.scope(this.media.textTracks).on("change",(()=>{const{textTracks:e}=t,{nextTextTrack:s}=this;let i=-1;for(let a=0;a<e.length;a+=1){const r=e[a];if(r.language===s?.language&&r.kind===s?.kind||isIOSFullscreen(t)||(r.mode="disabled"),"disabled"!==r.mode&&i>=0&&(r.mode="disabled"),"disabled"!==r.mode){if(i=a,!r.native&&!isIOSFullscreen(t)){const t=this.options.hideActiveTrack?"hidden":"showing";r.mode!==t&&(r.mode=t)}r.native&&r.language===s?.language&&r.kind===s.kind&&(r.mode="showing")}}const a=e[i]&&(this.textTrackList[i]??{id:e[i].id,kind:e[i].kind,language:e[i].language,label:e[i].label});this.textTrack?.language===a?.language&&this.textTrack?.kind===a?.kind||(this.textTrack=a,this.nextTextTrack=this.textTrack,this.emit(this.Events.CurrentTextTrackChanged,{textTrack:this.textTrack,index:i}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackchange",{textTrack:this.textTrack,index:i}))})).on("addtrack",(({track:t})=>t&&a(t))).on("removetrack",(({track:t})=>t&&r(t))),this.media.textTracks.length>0){this.textTrackList=parseTextTracks(t),i=!0;try{Array.prototype.forEach.call(this.media.textTracks,a),s()}finally{i=!1}}}getTextTracks(){return this.textTrackList}setTextTracks(t){const e=this.getMediaElement();if(this.eventListeners.scope(e,"@@setTextTracks").removeAllListeners(),this.addedTracks.forEach((t=>t.remove())),0===t.length)return;const s=()=>{addTextTracks(e,t,(t=>this.addedTracks.push(t))),this.textTrackList=parseTextTracks(e)};e.readyState>=e.HAVE_CURRENT_DATA?s():this.eventListeners.scope(e,"@@setTextTracks").once("loadeddata",s)}getActiveTextTrack(){return this.textTrack}setActiveTextTrack(t){const e=this.getMediaElement();return this.nextTextTrack=t&&this.textTrackList.find((e=>e.language===t.language&&(!t.kind||e.kind===t.kind))),setActiveTextTrack(e,this.nextTextTrack,this.options.hideActiveTrack&&!isIOSFullscreen(e))}destroy(){this.detach(),this.removeAllListeners()}[Symbol.dispose](){this.destroy()}}!function(t){let e;!function(t){t.TextTrackListChanged="TrackListChanged",t.CurrentTextTrackChanged="CurrentTextTrackChanged",t.TextTrackCueChanged="TextTrackCueChanged"}(e=t.Events||(t.Events={}))}(TextTracksController||(TextTracksController={}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@js-toolkit/web-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.61.0",
|
|
4
4
|
"description": "Web utils",
|
|
5
5
|
"author": "VZH",
|
|
6
6
|
"license": "MIT",
|
|
@@ -20,32 +20,31 @@
|
|
|
20
20
|
"@js-toolkit/node-utils": "^1.2.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@eslint/compat": "^1.2.
|
|
23
|
+
"@eslint/compat": "^1.2.6",
|
|
24
24
|
"@eslint/eslintrc": "^3.2.0",
|
|
25
|
-
"@eslint/js": "^9.
|
|
25
|
+
"@eslint/js": "^9.20.0",
|
|
26
26
|
"@js-toolkit/configs": "^3.94.0",
|
|
27
|
-
"@js-toolkit/utils": "^1.
|
|
27
|
+
"@js-toolkit/utils": "^1.59.0",
|
|
28
28
|
"@types/eslint": "^9.6.1",
|
|
29
29
|
"@types/eslint__eslintrc": "^2.1.2",
|
|
30
30
|
"@types/eslint__js": "^8.42.3",
|
|
31
31
|
"@types/lodash.throttle": "^4.1.9",
|
|
32
32
|
"@types/uuid": "^10.0.0",
|
|
33
33
|
"copyfiles": "^2.4.1",
|
|
34
|
-
"eslint": "^9.
|
|
34
|
+
"eslint": "^9.20.1",
|
|
35
35
|
"eslint-config-prettier": "^10.0.1",
|
|
36
36
|
"eslint-plugin-import": "^2.31.0",
|
|
37
37
|
"eslint-plugin-prettier": "^5.2.3",
|
|
38
|
-
"eventemitter3": "^5.0.1",
|
|
39
38
|
"lodash.throttle": "^4.1.1",
|
|
40
|
-
"prettier": "^3.
|
|
39
|
+
"prettier": "^3.5.1",
|
|
41
40
|
"reconnecting-websocket": "^4.4.0",
|
|
42
41
|
"rimraf": "^6.0.1",
|
|
43
|
-
"terser": "^5.
|
|
42
|
+
"terser": "^5.39.0",
|
|
44
43
|
"typescript": "^5.7.3",
|
|
45
|
-
"typescript-eslint": "^8.
|
|
46
|
-
"ua-parser-js": "^2.0.
|
|
44
|
+
"typescript-eslint": "^8.24.0",
|
|
45
|
+
"ua-parser-js": "^2.0.2",
|
|
47
46
|
"uuid": "^11.0.5",
|
|
48
|
-
"webpack": "^5.
|
|
47
|
+
"webpack": "^5.98.0",
|
|
49
48
|
"yargs": "^17.7.2"
|
|
50
49
|
}
|
|
51
50
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isTelegramWebView(){return!!window.TelegramWebviewProxy}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
2
|
import { ErrorCompat } from '@js-toolkit/utils/ErrorCompat';
|
|
3
3
|
export declare class ServiceWorkerUnavailableError extends ErrorCompat {
|
|
4
4
|
constructor();
|
|
5
5
|
}
|
|
6
|
-
export declare class ServiceWorkerInstaller extends
|
|
6
|
+
export declare class ServiceWorkerInstaller extends EventEmitter<{
|
|
7
7
|
registered: [{
|
|
8
8
|
registration: ServiceWorkerRegistration;
|
|
9
9
|
}];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{ErrorCompat}from"@js-toolkit/utils/ErrorCompat";import{onPageReady}from"../onPageReady";import{isLocalhost}from"./utils";export class ServiceWorkerUnavailableError extends ErrorCompat{constructor(){super(ServiceWorkerUnavailableError,"ServiceWorker is not available",{name:"ServiceWorkerUnavailableError"})}}export class ServiceWorkerInstaller extends EventEmitter{static isAvailable(){return"serviceWorker"in navigator}registration;cancelDefferedRegister;constructor(){super()}register(r,e){if(!ServiceWorkerInstaller.isAvailable())throw new ServiceWorkerUnavailableError;const{deffered:t,...i}=e??{},o=async()=>{try{if(isLocalhost()){const e=await fetch(r);if(!e.ok)throw new Error(`No service worker found at '${e.url}'.`,{cause:`Response: ${e.status} ${e.statusText}`})}const e=await navigator.serviceWorker.register(r,i);this.registration=e,this.emit("registered",{registration:e}),e.onupdatefound=()=>{const r=e.installing;null!=r&&(r.onstatechange=()=>{"installed"===r.state&&(navigator.serviceWorker.controller?this.emit("updatePending",{registration:e}):this.emit("updated",{registration:e}))},r.onerror=r=>{const e=new Error("Error during service worker installation",{cause:r});this.emit("error",{error:e})})}}catch(r){const e=new Error("Error during service worker registration",{cause:r});this.emit("error",{error:e})}};t?this.cancelDefferedRegister=onPageReady(o,"number"==typeof t?{timeout:t}:void 0):o()}unregister(){this.cancelDefferedRegister&&this.cancelDefferedRegister(),this.registration?.unregister().catch((r=>{const e=new Error("Error during service worker unregister",{cause:r});this.emit("error",{error:e})}))}destroy(){this.cancelDefferedRegister&&this.cancelDefferedRegister(),this.removeAllListeners()}[Symbol.dispose](){this.destroy()}}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
2
|
+
import log from '@js-toolkit/utils/log';
|
|
2
3
|
import * as sdpUtils from './sdputils';
|
|
3
|
-
export declare class PeerConnection extends
|
|
4
|
+
export declare class PeerConnection extends EventEmitter<PeerConnection.EventMap, PeerConnection> implements Disposable {
|
|
4
5
|
readonly options: PeerConnection.Options;
|
|
5
6
|
get Events(): typeof PeerConnection.Events;
|
|
6
|
-
readonly logger: Pick<
|
|
7
|
+
readonly logger: Pick<log.Logger, "warn" | "debug">;
|
|
7
8
|
private pc;
|
|
8
9
|
constructor(options?: PeerConnection.Options);
|
|
9
10
|
private clear;
|
|
@@ -38,7 +39,7 @@ export declare namespace PeerConnection {
|
|
|
38
39
|
readonly offerOptions?: RTCOfferOptions;
|
|
39
40
|
readonly rtc?: RTCConfiguration;
|
|
40
41
|
readonly codecs?: sdpUtils.PreferCodecs;
|
|
41
|
-
readonly logger?: Pick<
|
|
42
|
+
readonly logger?: Pick<log.Logger, 'debug' | 'warn'> | undefined;
|
|
42
43
|
readonly id?: string;
|
|
43
44
|
}
|
|
44
45
|
enum Events {
|
|
@@ -53,7 +54,7 @@ export declare namespace PeerConnection {
|
|
|
53
54
|
ReinitializingConnectionRequired = "ReinitializingConnectionRequired",
|
|
54
55
|
Closed = "Closed"
|
|
55
56
|
}
|
|
56
|
-
type EventMap = DataEventMap<DefineAll<Events, {
|
|
57
|
+
type EventMap = EventEmitter.DataEventMap<DefineAll<Events, {
|
|
57
58
|
[Events.LocalIceCandidate]: [RTCIceCandidate];
|
|
58
59
|
[Events.EndOfIceCandidates]: [];
|
|
59
60
|
[Events.Connected]: [];
|
|
@@ -62,7 +63,7 @@ export declare namespace PeerConnection {
|
|
|
62
63
|
[Events.ReinitializingConnectionRequired]: [];
|
|
63
64
|
[Events.Closed]: [];
|
|
64
65
|
}>, PeerConnection>;
|
|
65
|
-
type EventHandler<T extends Events = Events> = DataEventListener<EventMap, T, PeerConnection>;
|
|
66
|
+
type EventHandler<T extends Events = Events> = EventEmitter.DataEventListener<EventMap, T, PeerConnection>;
|
|
66
67
|
type EventHandlerMap<T extends Events = Events> = {
|
|
67
68
|
[P in T]: EventHandler<P>;
|
|
68
69
|
};
|
package/webrtc/PeerConnection.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{getErrorMessage}from"@js-toolkit/utils/getErrorMessage";import{hasIn}from"@js-toolkit/utils/hasIn";import log from"@js-toolkit/utils/log";import*as sdpUtils from"./sdputils";export class PeerConnection extends EventEmitter{options;get Events(){return PeerConnection.Events}logger;pc;constructor(e={}){super(),this.options=e,this.logger=e.logger??log.getLogger("PeerConnection"),this.pc=this.createPC()}clear(){this.pc.onsignalingstatechange=null,this.pc.onconnectionstatechange=null,this.pc.oniceconnectionstatechange=null,this.pc.ontrack=null,this.pc.onnegotiationneeded=null,this.pc.onicecandidate=null,this.pc.onicecandidateerror=null}createPC(){const e=new RTCPeerConnection(this.options.rtc);return e.onsignalingstatechange=()=>{this.logger.debug(`Signaling state changed to: ${e.signalingState}`),"closed"===e.signalingState&&(this.emit(this.Events.Closed),this.clear())},hasIn(RTCPeerConnection.prototype,"onconnectionstatechange")?e.onconnectionstatechange=()=>{this.logger.debug(`Connection state changed to: ${e.connectionState}`),"connected"===e.connectionState?this.emit(this.Events.Connected):"disconnected"===e.connectionState&&this.emit(this.Events.Disconnected)}:e.oniceconnectionstatechange=()=>{this.logger.debug(`ICE connection state changed to: ${e.iceConnectionState}`),"connected"===e.iceConnectionState?this.emit(this.Events.Connected):"disconnected"===e.iceConnectionState&&this.emit(this.Events.Disconnected)},e.ontrack=({streams:e,track:t})=>{if(this.logger.debug("Remote stream received.",e.length,t.kind),0===e.length)return;const[n]=e;n.onremovetrack=()=>{this.logger.debug("onremovetrack"),this.emit(this.Events.RemoteStreamChanged,n)},n.onaddtrack=()=>{this.logger.debug("onaddtrack"),this.emit(this.Events.RemoteStreamChanged,n)},this.emit(this.Events.RemoteStreamChanged,n)},e.onnegotiationneeded=()=>{const{iceConnectionState:t}=e;"connected"!==t&&"completed"!==t||(this.logger.debug("Reinitializing connection..."),this.emit(this.Events.ReinitializingConnectionRequired))},e.onicecandidate=({candidate:e})=>{e?sdpUtils.isValidIceCandidate(e)&&this.emit(this.Events.LocalIceCandidate,e):this.emit(this.Events.EndOfIceCandidates)},e.onicecandidateerror=e=>{if(e instanceof RTCPeerConnectionIceErrorEvent){const{errorCode:t,errorText:n,hostCandidate:i,url:o}=e;this.logger.warn(`ICE candidate error: errorCode=${t}, errorText=${n}, hostCandidate=${i}, url=${o}`)}else this.logger.warn(`ICE candidate error: ${getErrorMessage(e)}`)},e}async setLocalDescription(e,t){const n=sdpUtils.prepareLocalDescription(t,this.options.codecs);return await e.setLocalDescription(n),n}async setRemoteDescription(e,t){const n=sdpUtils.prepareRemoteDescription(t,this.options.codecs);return await e.setRemoteDescription(n),n}isConnected(){return"connected"===this.pc.iceConnectionState||"completed"===this.pc.iceConnectionState}isClosed(){return"closed"===this.pc.signalingState}addIceCandidate(e){return this.pc.addIceCandidate(e)}attachStream(e){const t=this.pc.getSenders(),n=t.length?e.getTracks().filter((e=>!t.find((({track:t})=>!!t&&t.id===e.id)))):e.getTracks();n.length?(n.forEach((t=>this.pc.addTrack(t,e))),this.logger.debug(`Attached ${n.length} track(s) to a peer connection.`)):this.logger.debug("No tracks to attach to a peer connection.")}reattachStream(e){const t=this.pc.iceConnectionState;if("new"!==t&&"connected"!==t&&"completed"!==t)return;const n=e?e.getTracks():[];this.pc.getSenders().forEach((e=>{const{track:t}=e;!t||"ended"!==t.readyState&&n.find((e=>e.id===t.id))||(this.pc.removeTrack(e),this.logger.debug(`'${t&&t.kind}' track is removed from a peer connection.`))})),e&&this.attachStream(e)}async createOffer(){const e=await this.pc.createOffer(this.options.offerOptions);return this.setLocalDescription(this.pc,e)}async createAnswer(e){await this.setRemoteDescription(this.pc,e);const t=await this.pc.createAnswer(this.options.offerOptions);return this.setLocalDescription(this.pc,t)}applyAnswer(e){return this.setRemoteDescription(this.pc,e)}reconnect(){this.close(),this.pc=this.createPC()}close(){this.pc.close(),this.pc.onsignalingstatechange&&(this.emit(this.Events.Closed),this.clear())}destroy(){this.close(),this.removeAllListeners()}[Symbol.dispose](){this.destroy()}}!function(e){let t;!function(e){e.LocalIceCandidate="LocalIceCandidate",e.EndOfIceCandidates="EndOfIceCandidates",e.Connected="Connected",e.Disconnected="Disconnected",e.RemoteStreamChanged="RemoteStreamChanged",e.ReinitializingConnectionRequired="ReinitializingConnectionRequired",e.Closed="Closed"}(t=e.Events||(e.Events={}))}(PeerConnection||(PeerConnection={}));
|
package/ws/WSController.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ReconnectingWebSocket, { type ErrorEvent, type Message, type Options as BaseOptions, type UrlProvider } from 'reconnecting-websocket';
|
|
2
|
-
import {
|
|
3
|
-
export declare class WSController<TData = unknown> extends
|
|
2
|
+
import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
|
|
3
|
+
export declare class WSController<TData = unknown> extends EventEmitter<WSController.EventMap<TData>, WSController<TData>> implements Disposable {
|
|
4
4
|
get Events(): typeof WSController.Events;
|
|
5
5
|
readonly logger: NonNullable<WSController.Options['logger']>;
|
|
6
6
|
protected readonly ws: ReconnectingWebSocket;
|
|
@@ -37,7 +37,7 @@ export declare namespace WSController {
|
|
|
37
37
|
/** If connection was closed and the object will not to try to reconnect */
|
|
38
38
|
Closed = "Closed"
|
|
39
39
|
}
|
|
40
|
-
type EventMap<TData = unknown> = DataEventMap<DefineAll<Events, {
|
|
40
|
+
type EventMap<TData = unknown> = EventEmitter.DataEventMap<DefineAll<Events, {
|
|
41
41
|
[Events.Connected]: [];
|
|
42
42
|
[Events.Message]: [
|
|
43
43
|
event: OmitStrict<MessageEvent<TData>, keyof Event | 'initMessageEvent'>
|
|
@@ -47,5 +47,5 @@ export declare namespace WSController {
|
|
|
47
47
|
readonly reason: string;
|
|
48
48
|
} | undefined];
|
|
49
49
|
}>, WSController<TData>>;
|
|
50
|
-
type EventHandler<T extends Events = Events, TData = unknown> = DataEventListener<EventMap<TData>, T, WSController<TData>>;
|
|
50
|
+
type EventHandler<T extends Events = Events, TData = unknown> = EventEmitter.DataEventListener<EventMap<TData>, T, WSController<TData>>;
|
|
51
51
|
}
|
package/ws/WSController.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import ReconnectingWebSocket,{}from"reconnecting-websocket";import{
|
|
1
|
+
import ReconnectingWebSocket,{}from"reconnecting-websocket";import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{delayed}from"@js-toolkit/utils/delayed";import{EventEmitterListener}from"../EventEmitterListener";function getNotConnectedError(){return new Error("The object is not connected yet.")}export class WSController extends EventEmitter{get Events(){return WSController.Events}logger;ws;listener;reconnectOnIdle;halfOpen;constructor(e,t){super();const{logger:n,protocols:s,binaryType:o,idleTimeout:r,halfOpenDetection:i,startClosed:c,...l}=t??{};this.logger=n??console,this.ws=new ReconnectingWebSocket(e,s,{...l,startClosed:!0}),null!=o&&(this.ws.binaryType=o),this.listener=new EventEmitterListener(this.ws),r&&r>0&&(this.reconnectOnIdle=delayed((()=>{this.logger.info(`WS connection reconnecting after ${r}ms due to inactivity.`),this.ws.reconnect()}),r)),this.halfOpen=(()=>{if(!i)return;const{pingTimeout:e=12e3,outMessage:t=()=>JSON.stringify({type:"pong"}),inMessageFilter:n=e=>e&&"object"==typeof e&&"ping"===e.type}=i,s=delayed((()=>{this.logger.info(`WS connection reconnecting after ${e}ms due to the probability of a half-open connection.`),this.ws.reconnect()}),e),o=()=>{s(),this.ws.send("function"!=typeof t||t instanceof Blob?t:t())};return{heartbeat:o,cancel:()=>s.cancel(),onMessage:({data:e})=>{n(e)?o():s()}}})(),this.listener.on("open",(()=>{this.reconnectOnIdle&&this.reconnectOnIdle(),this.halfOpen?.heartbeat(),this.emit(this.Events.Connected)})).on("close",(({code:e})=>{this.reconnectOnIdle?.cancel(),this.halfOpen?.cancel(),null!=t?.maxRetries&&this.ws.readyState===this.ws.CLOSED&&this.ws.retryCount===t.maxRetries&&this.close(e,`Failed to connect after ${t.maxRetries} attempts.`)})).on("message",(e=>{this.reconnectOnIdle&&this.reconnectOnIdle(),this.halfOpen?.onMessage(e),this.emit(this.Events.Message,e)})).on("error",(({error:e,message:t})=>{this.emit(this.Events.Error,{error:e,message:t})})),this.halfOpen&&(this.isConnected()||this.ws.readyState===this.ws.CONNECTING)&&this.halfOpen.heartbeat(),c||this.connect()}isConnected(){return this.ws.readyState===this.ws.OPEN}getUrl(){return this.ws.url}connect(){this.ws.reconnect()}send(e){if(!this.ws)throw getNotConnectedError();this.ws.send(e)}close(e,t){const n=!!this.ws;try{this.reconnectOnIdle&&this.reconnectOnIdle.cancel(),this.halfOpen&&this.halfOpen.cancel(),this.ws.close(e,t)}catch(e){this.logger.warn(e)}n&&this.emit(this.Events.Closed,t?{reason:t}:void 0)}destroy(){this.close(),this.listener.removeAllListeners(),this.removeAllListeners()}[Symbol.dispose](){this.destroy()}}!function(e){let t;!function(e){e.Connected="Connected",e.Message="Message",e.Error="Error",e.Closed="Closed"}(t=e.Events||(e.Events={}))}(WSController||(WSController={}));
|