@vkontakte/videoplayer-core 2.0.179-dev.b26d838aa.0 → 2.0.179-dev.e57738f45.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.
Files changed (43) hide show
  1. package/es2015.cjs +16 -16
  2. package/es2015.esm.js +16 -16
  3. package/es2015.index-cr5tw2xy.esm.js +6 -0
  4. package/es2015.index-t72w9xv7.esm.js +6 -0
  5. package/es2015.index-wknsr6rg.esm.js +6 -0
  6. package/esnext.cjs +15 -15
  7. package/esnext.esm.js +15 -15
  8. package/esnext.index-2891gk74.esm.js +6 -0
  9. package/esnext.index-cyvrx4m5.esm.js +6 -0
  10. package/esnext.index-hbbvp6j9.esm.js +6 -0
  11. package/evergreen.esm.js +15 -15
  12. package/evergreen.index-15ghkwj6.esm.js +6 -0
  13. package/evergreen.index-cbv5dxy5.esm.js +6 -0
  14. package/evergreen.index-hh52jbk9.esm.js +6 -0
  15. package/package.json +2 -2
  16. package/types/index.d.ts +6 -0
  17. package/types/lazy-logger/index.d.ts +3 -0
  18. package/types/lazy-worker/index.d.ts +1 -0
  19. package/types/lazy-worker/worker.d.ts +1 -0
  20. package/types/player/Player.d.ts +1 -1
  21. package/types/player/types.d.ts +2 -0
  22. package/types/providers/DashProvider/lib/buffer.d.ts +2 -0
  23. package/types/providers/DashProvider/lib/fetcher.d.ts +9 -4
  24. package/types/providers/DashProvider/lib/player.d.ts +1 -0
  25. package/types/providers/DashProviderVirtual/lib/buffer/types.d.ts +2 -0
  26. package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/baseVirtualBufferManager.d.ts +3 -1
  27. package/types/providers/DashProviderVirtual/lib/buffer/virtualBuffer/byteRangeVirtualBufferManager.d.ts +1 -0
  28. package/types/providers/DashProviderVirtual/lib/fetcher.d.ts +9 -4
  29. package/types/providers/DashProviderVirtual/lib/player/basePlayer.d.ts +5 -2
  30. package/types/providers/ProviderContainer/index.d.ts +3 -0
  31. package/types/providers/types.d.ts +2 -2
  32. package/types/providers/utils/segmentCache/IndexedDbSegmentCacheStorage.d.ts +54 -0
  33. package/types/providers/utils/segmentCache/MediaSegmentBatchCache.d.ts +59 -0
  34. package/types/providers/utils/segmentCache/constants.d.ts +20 -0
  35. package/types/providers/utils/segmentCache/index.d.ts +8 -0
  36. package/types/providers/utils/segmentCache/segmentCacheDb.d.ts +25 -0
  37. package/types/providers/utils/segmentCache/segmentCacheUtils.d.ts +25 -0
  38. package/types/providers/utils/segmentCache/types.d.ts +85 -0
  39. package/types/utils/buffer/isPositionBuffered.d.ts +1 -0
  40. package/types/utils/tuningConfig.d.ts +3 -9
  41. package/videoplayer-lazy-worker.esm.js +6 -0
  42. package/types/providers/DashProvider/lib/urlsCache.d.ts +0 -23
  43. package/types/providers/DashProviderVirtual/lib/urlsCache.d.ts +0 -22
@@ -0,0 +1,85 @@
1
+ import type { Byte, IRange } from "@vkontakte/videoplayer-shared";
2
+ import type { CacheEntryType } from "./constants";
3
+ export type InitSegmentRangeType = "init" | "index";
4
+ export interface SegmentCacheEntry {
5
+ data: ArrayBuffer;
6
+ segmentIndex: number;
7
+ }
8
+ export interface InitSegmentParams {
9
+ normalizedUrl: string;
10
+ rangeType: InitSegmentRangeType;
11
+ range: IRange<Byte>;
12
+ }
13
+ export interface SetMediaSegmentParams {
14
+ type: "media";
15
+ normalizedUrl: string;
16
+ segmentIndex: number;
17
+ data: ArrayBuffer;
18
+ }
19
+ export interface SetInitSegmentParams {
20
+ type: "init";
21
+ normalizedUrl: string;
22
+ rangeType: InitSegmentRangeType;
23
+ range: IRange<Byte>;
24
+ data: ArrayBuffer;
25
+ }
26
+ export type SetSegmentParams = SetMediaSegmentParams | SetInitSegmentParams;
27
+ export interface ISegmentCacheStorage {
28
+ getSegments(normalizedUrl: string, indices: number[]): Promise<Map<number, ArrayBuffer>>;
29
+ setSegment(params: SetSegmentParams): Promise<void>;
30
+ /** Удаляет media-записи по индексам сегментов. Используется для самолечения: запись с несовпадающей длиной удаляется и сегмент перекачивается */
31
+ deleteSegments(normalizedUrl: string, indices: number[]): Promise<void>;
32
+ getInitSegment(params: InitSegmentParams): Promise<ArrayBuffer | null>;
33
+ /** Полная очистка кэша (все записи, без привязки к url) с рассылкой события другим инстансам */
34
+ clear(): Promise<void>;
35
+ destroy(): void;
36
+ }
37
+ export interface CacheMetadataEntry {
38
+ index: number;
39
+ byteRange: IRange<Byte>;
40
+ }
41
+ export interface CacheMetadata {
42
+ segments: CacheMetadataEntry[];
43
+ }
44
+ /**
45
+ * Режим кэширования в IndexedDB:
46
+ * - 'none' — кэш выключен, сервис не создаётся;
47
+ * - 'all' — кэшируются media-сегменты и init/index;
48
+ * - 'init' — кэшируются только init/index: фетчер не создаёт media-обёртку MediaSegmentBatchCache,
49
+ * сервис используется напрямую для init/index. Media-записи, оставшиеся в базе от режима
50
+ * 'all', не читаются и не перезаписываются — эксперимент по init-кэшу должен быть чистым.
51
+ */
52
+ export type SegmentCacheMode = "none" | "all" | "init";
53
+ export interface SegmentCacheConfig {
54
+ /** Режим кэширования: 'all' — сегменты и init/index, 'init' — только init/index, 'none' — выключен */
55
+ mode: SegmentCacheMode;
56
+ /** Лимит суммарного размера кэша (media + init/index сегменты) в байтах. При QuotaExceededError автоматически опускается до текущего размера */
57
+ maxTotalSizeBytes: number;
58
+ /** Доля от эффективного лимита, при превышении которой запускается LRU-эвикт (0.9 = 90%) */
59
+ evictionThreshold: number;
60
+ /** Доля от максимального хранимого кэша, до которой LRU-эвикт очищает кэш (0.7 = 70%).
61
+ * Должна быть ниже evictionThreshold: запас между ними не даёт эвикту срабатывать на каждую запись у границы лимита
62
+ * */
63
+ evictionTargetPercent: number;
64
+ /** Максимальный объём данных в байтах, который LRU-эвикт удаляет за один проход (ограничивает длительность одной транзакции) */
65
+ evictionBatchMaxBytes: number;
66
+ /** Доля от maxTotalStoredBytes, которую нужно освободить при QuotaExceededError (0.5 = 50%) */
67
+ quotaExceededEvictPercent: number;
68
+ /** TTL для media-сегментов в миллисекундах. Протухшие записи удаляются при чтении и при rehydrate */
69
+ ttlMs: number;
70
+ /** TTL для init/index-сегментов в миллисекундах (обычно длиннее, т.к. они стабильнее) */
71
+ initTtlMs: number;
72
+ /** Стратегия очистки кэша при ошибках декодера/фетчера: 'once' — один раз за жизнь контейнера, 'always' — на каждую ошибку */
73
+ clearOnErrorStrategy: "once" | "always";
74
+ }
75
+ /**
76
+ * Единая запись кэша. Ключ передаётся явно (out-of-line) через createMediaKey / createInitKey.
77
+ */
78
+ export interface CacheRecord {
79
+ normalizedUrl: string;
80
+ data: ArrayBuffer;
81
+ byteLength: number;
82
+ createdAt: number;
83
+ lastAccessAt: number;
84
+ }
85
+ export type CachePrimaryKey = [string, CacheEntryType, number, number];
@@ -1,3 +1,4 @@
1
1
  import type { Milliseconds } from "@vkontakte/videoplayer-shared";
2
2
  declare const _default: (timeRanges: TimeRanges, position: Milliseconds, tolerance?: number) => boolean;
3
3
  export default _default;
4
+ export declare const isTimeRangeBuffered: (timeRanges: TimeRanges, from: Milliseconds, to: Milliseconds) => boolean;
@@ -5,6 +5,7 @@ import { IOSPreferredFormat } from "../enums/IOSPreferredFormat";
5
5
  import type { Byte, Kbps, Milliseconds, RecursivePartial, Seconds, TimerModule } from "@vkontakte/videoplayer-shared";
6
6
  import { type ExactVideoQuality, VideoQuality } from "@vkontakte/videoplayer-shared";
7
7
  import { AudioRuleName, VideoRuleName } from "../providers/utils/Abr/types";
8
+ import type { SegmentCacheConfig } from "../providers/utils/segmentCache/types";
8
9
  export type ITuningConfig = {
9
10
  /** @deprecated */
10
11
  configName?: string[];
@@ -286,6 +287,7 @@ export type ITuningConfig = {
286
287
  * При `true` — эксперимент: `switchMutex` не блокирует `maintainNativeBuffer`.
287
288
  */
288
289
  enableMaintainNativeBufferSwitchMutexBypass: boolean;
290
+ skipReloadOfFullyBufferedPartiallyEjectedSegments: boolean;
289
291
  };
290
292
  dashCmafLive: {
291
293
  externalStopControl: boolean;
@@ -472,15 +474,6 @@ export type ITuningConfig = {
472
474
  */
473
475
  handleExtendedNetworkErrorsSet?: boolean;
474
476
  /**
475
- * Кэшируем ответы от сервера руками, так как из за динамичесокй природы некоторых параметров в урле
476
- * браузер это не может сделать.
477
- */
478
- useUrlCacheMechanism?: boolean;
479
- /**
480
- * Очищаем кэш урлов при падение провайдера.
481
- */
482
- dropUrlCacheWhenProviderCrashed?: boolean;
483
- /**
484
477
  * При смене/реините провайдера игнорируем результат того, смогло видео заиграть или нет.
485
478
  */
486
479
  ignoreForcePlayResultWhenProviderChanged?: boolean;
@@ -508,6 +501,7 @@ export type ITuningConfig = {
508
501
  * провайдер по-прежнему ждёт canplay, чтобы play() не вызывался до появления данных.
509
502
  */
510
503
  readyOnLoadedMetadata: boolean;
504
+ segmentCache: SegmentCacheConfig;
511
505
  };
512
506
  export type IOptionalTuningConfig = RecursivePartial<ITuningConfig>;
513
507
  export declare const fillDefault: (partial: IOptionalTuningConfig) => ITuningConfig;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @vkontakte/videoplayer-core v2.0.179-dev.e57738f45.0
3
+ * Wed, 14 Oct 2026 08:33:51 GMT
4
+ * https://st.mycdn.me/static/vkontakte-videoplayer/2-0-179/doc/
5
+ */
6
+ var e=Object.create,t=Object.getPrototypeOf,n=Object.defineProperty,r=Object.getOwnPropertyNames,i=Object.prototype.hasOwnProperty;function a(e){return this[e]}var o,s,c=((c,l,u)=>{var d=typeof c==`object`&&!!c;if(d){var f=l?o??=new WeakMap:s??=new WeakMap,p=f.get(c);if(p)return p}u=c==null?{}:e(t(c));let m=l||!c||!c.__esModule||!i.call(c,`default`)?n(u,`default`,{value:c,enumerable:!0}):u;if(c&&typeof c==`object`||typeof c==`function`)for(let e of r(c))i.call(m,e)||n(m,e,{get:a.bind(c,e),enumerable:!0});return d&&f.set(c,m),m})(((e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports))(function(e,t){(function(n,r){typeof e==`object`&&t!==void 0?t.exports=r():typeof define==`function`&&define.amd?define(r):(n=typeof globalThis<`u`?globalThis:n||self).dayjs=r()})(e,function(){var e=1e3,t=6e4,n=36e5,r=`millisecond`,i=`second`,a=`minute`,o=`hour`,s=`day`,c=`week`,l=`month`,u=`quarter`,d=`year`,f=`date`,p=`Invalid Date`,m=/^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/,h=/\[([^\]]+)]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,g={name:`en`,weekdays:`Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday`.split(`_`),months:`January_February_March_April_May_June_July_August_September_October_November_December`.split(`_`),ordinal:function(e){var t=[`th`,`st`,`nd`,`rd`],n=e%100;return`[`+e+(t[(n-20)%10]||t[n]||t[0])+`]`}},_=function(e,t,n){var r=String(e);return!r||r.length>=t?e:``+Array(t+1-r.length).join(n)+e},v={s:_,z:function(e){var t=-e.utcOffset(),n=Math.abs(t),r=Math.floor(n/60),i=n%60;return(t<=0?`+`:`-`)+_(r,2,`0`)+`:`+_(i,2,`0`)},m:function e(t,n){if(t.date()<n.date())return-e(n,t);var r=12*(n.year()-t.year())+(n.month()-t.month()),i=t.clone().add(r,l),a=n-i<0,o=t.clone().add(r+(a?-1:1),l);return+(-(r+(n-i)/(a?i-o:o-i))||0)},a:function(e){return e<0?Math.ceil(e)||0:Math.floor(e)},p:function(e){return{M:l,y:d,w:c,d:s,D:f,h:o,m:a,s:i,ms:r,Q:u}[e]||String(e||``).toLowerCase().replace(/s$/,``)},u:function(e){return e===void 0}},y=`en`,b={};b[y]=g;var x=`$isDayjsObject`,S=function(e){return e instanceof E||!(!e||!e[x])},C=function e(t,n,r){var i;if(!t)return y;if(typeof t==`string`){var a=t.toLowerCase();b[a]&&(i=a),n&&(b[a]=n,i=a);var o=t.split(`-`);if(!i&&o.length>1)return e(o[0])}else{var s=t.name;b[s]=t,i=s}return!r&&i&&(y=i),i||!r&&y},w=function(e,t){if(S(e))return e.clone();var n=typeof t==`object`?t:{};return n.date=e,n.args=arguments,new E(n)},T=v;T.l=C,T.i=S,T.w=function(e,t){return w(e,{locale:t.$L,utc:t.$u,x:t.$x,$offset:t.$offset})};var E=function(){function g(e){this.$L=C(e.locale,null,!0),this.parse(e),this.$x=this.$x||e.x||{},this[x]=!0}var _=g.prototype;return _.parse=function(e){this.$d=function(e){var{date:t,utc:n}=e;if(t===null)return new Date(NaN);if(T.u(t))return new Date;if(t instanceof Date)return new Date(t);if(typeof t==`string`&&!/Z$/i.test(t)){var r=t.match(m);if(r){var i=r[2]-1||0,a=(r[7]||`0`).substring(0,3);return n?new Date(Date.UTC(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,a)):new Date(r[1],i,r[3]||1,r[4]||0,r[5]||0,r[6]||0,a)}}return new Date(t)}(e),this.init()},_.init=function(){var e=this.$d;this.$y=e.getFullYear(),this.$M=e.getMonth(),this.$D=e.getDate(),this.$W=e.getDay(),this.$H=e.getHours(),this.$m=e.getMinutes(),this.$s=e.getSeconds(),this.$ms=e.getMilliseconds()},_.$utils=function(){return T},_.isValid=function(){return this.$d.toString()!==p},_.isSame=function(e,t){var n=w(e);return this.startOf(t)<=n&&n<=this.endOf(t)},_.isAfter=function(e,t){return w(e)<this.startOf(t)},_.isBefore=function(e,t){return this.endOf(t)<w(e)},_.$g=function(e,t,n){return T.u(e)?this[t]:this.set(n,e)},_.unix=function(){return Math.floor(this.valueOf()/1e3)},_.valueOf=function(){return this.$d.getTime()},_.startOf=function(e,t){var n=this,r=!!T.u(t)||t,u=T.p(e),p=function(e,t){var i=T.w(n.$u?Date.UTC(n.$y,t,e):new Date(n.$y,t,e),n);return r?i:i.endOf(s)},m=function(e,t){return T.w(n.toDate()[e].apply(n.toDate(`s`),(r?[0,0,0,0]:[23,59,59,999]).slice(t)),n)},h=this.$W,g=this.$M,_=this.$D,v=`set`+(this.$u?`UTC`:``);switch(u){case d:return r?p(1,0):p(31,11);case l:return r?p(1,g):p(0,g+1);case c:var y=this.$locale().weekStart||0,b=(h<y?h+7:h)-y;return p(r?_-b:_+(6-b),g);case s:case f:return m(v+`Hours`,0);case o:return m(v+`Minutes`,1);case a:return m(v+`Seconds`,2);case i:return m(v+`Milliseconds`,3);default:return this.clone()}},_.endOf=function(e){return this.startOf(e,!1)},_.$set=function(e,t){var n,c=T.p(e),u=`set`+(this.$u?`UTC`:``),p=(n={},n[s]=u+`Date`,n[f]=u+`Date`,n[l]=u+`Month`,n[d]=u+`FullYear`,n[o]=u+`Hours`,n[a]=u+`Minutes`,n[i]=u+`Seconds`,n[r]=u+`Milliseconds`,n)[c],m=c===s?this.$D+(t-this.$W):t;if(c===l||c===d){var h=this.clone().set(f,1);h.$d[p](m),h.init(),this.$d=h.set(f,Math.min(this.$D,h.daysInMonth())).$d}else p&&this.$d[p](m);return this.init(),this},_.set=function(e,t){return this.clone().$set(e,t)},_.get=function(e){return this[T.p(e)]()},_.add=function(r,u){var f,p=this;r=Number(r);var m=T.p(u),h=function(e){var t=w(p);return T.w(t.date(t.date()+Math.round(e*r)),p)};if(m===l)return this.set(l,this.$M+r);if(m===d)return this.set(d,this.$y+r);if(m===s)return h(1);if(m===c)return h(7);var g=(f={},f[a]=t,f[o]=n,f[i]=e,f)[m]||1,_=this.$d.getTime()+r*g;return T.w(_,this)},_.subtract=function(e,t){return this.add(-1*e,t)},_.format=function(e){var t=this,n=this.$locale();if(!this.isValid())return n.invalidDate||p;var r=e||`YYYY-MM-DDTHH:mm:ssZ`,i=T.z(this),a=this.$H,o=this.$m,s=this.$M,{weekdays:c,months:l,meridiem:u}=n,d=function(e,n,i,a){return e&&(e[n]||e(t,r))||i[n].slice(0,a)},f=function(e){return T.s(a%12||12,e,`0`)},m=u||function(e,t,n){var r=e<12?`AM`:`PM`;return n?r.toLowerCase():r};return r.replace(h,function(e,r){return r||function(e){switch(e){case`YY`:return String(t.$y).slice(-2);case`YYYY`:return T.s(t.$y,4,`0`);case`M`:return s+1;case`MM`:return T.s(s+1,2,`0`);case`MMM`:return d(n.monthsShort,s,l,3);case`MMMM`:return d(l,s);case`D`:return t.$D;case`DD`:return T.s(t.$D,2,`0`);case`d`:return String(t.$W);case`dd`:return d(n.weekdaysMin,t.$W,c,2);case`ddd`:return d(n.weekdaysShort,t.$W,c,3);case`dddd`:return c[t.$W];case`H`:return String(a);case`HH`:return T.s(a,2,`0`);case`h`:return f(1);case`hh`:return f(2);case`a`:return m(a,o,!0);case`A`:return m(a,o,!1);case`m`:return String(o);case`mm`:return T.s(o,2,`0`);case`s`:return String(t.$s);case`ss`:return T.s(t.$s,2,`0`);case`SSS`:return T.s(t.$ms,3,`0`);case`Z`:return i}return null}(e)||i.replace(`:`,``)})},_.utcOffset=function(){return 15*-Math.round(this.$d.getTimezoneOffset()/15)},_.diff=function(r,f,p){var m,h=this,g=T.p(f),_=w(r),v=(_.utcOffset()-this.utcOffset())*t,y=this-_,b=function(){return T.m(h,_)};switch(g){case d:m=b()/12;break;case l:m=b();break;case u:m=b()/3;break;case c:m=(y-v)/6048e5;break;case s:m=(y-v)/864e5;break;case o:m=y/n;break;case a:m=y/t;break;case i:m=y/e;break;default:m=y}return p?m:T.a(m)},_.daysInMonth=function(){return this.endOf(l).$D},_.$locale=function(){return b[this.$L]},_.locale=function(e,t){if(!e)return this.$L;var n=this.clone(),r=C(e,t,!0);return r&&(n.$L=r),n},_.clone=function(){return T.w(this.$d,this)},_.toDate=function(){return new Date(this.valueOf())},_.toJSON=function(){return this.isValid()?this.toISOString():null},_.toISOString=function(){return this.$d.toISOString()},_.toString=function(){return this.$d.toUTCString()},g}(),D=E.prototype;return w.prototype=D,[[`$ms`,r],[`$s`,i],[`$m`,a],[`$H`,o],[`$W`,s],[`$M`,l],[`$y`,d],[`$D`,f]].forEach(function(e){D[e[1]]=function(t){return this.$g(t,e[0],e[1])}}),w.extend=function(e,t){return e.$i||=(e(t,E,w),!0),w},w.locale=C,w.isDayjs=S,w.unix=function(e){return w(1e3*e)},w.en=b[y],w.Ls=b,w.p={},w})})(),1),l=`Hello world from lazy chunk worker`,u=self;u.onmessage=e=>{e.data===`ping`&&u.postMessage({message:l,stamp:c.default().format(`YYYY-MM-DD`)})};
@@ -1,23 +0,0 @@
1
- export type CacheManager = {
2
- get: () => Promise<Response | null>;
3
- set: (response: Response) => void;
4
- };
5
- /**
6
- * Пытаемся добиться кэширования вызовов.
7
- * Сейчас есть проблема, что expires и sig меняются, хотя контент не меняется, поэтому ходим мимо кэша.
8
- * Из за этого обрабатываем кэш вручную.
9
- */
10
- export declare const createCacheManager: (url: string) => Promise<CacheManager>;
11
- export declare function canUseCacheApi(): boolean;
12
- export declare function normalizeUrl(url: string): string;
13
- /**
14
- * Проверяет, не просрочен ли кэшированный ответ
15
- */
16
- export declare function isResponseExpired(response: Response): boolean;
17
- export declare function cloneResponse(response: Response, url: string): Response;
18
- /**
19
- * Извлекает expires из оригинального URL (параметр expires1)
20
- * Предполагается, что expires1 — timestamp в секундах
21
- */
22
- export declare function getExpiresFromUrl(url: string): number;
23
- export declare const dropUrlsCache: () => Promise<void>;
@@ -1,22 +0,0 @@
1
- export type CacheManager = {
2
- get: () => Promise<Response | null>;
3
- set: (response: Response) => Promise<void>;
4
- };
5
- /**
6
- * Пытаемся добиться кэширования вызовов.
7
- * Сейчас есть проблема, что expires и sig меняются, хотя контент не меняется, поэтому ходим мимо кэша.
8
- * Из за этого обрабатываем кэш вручную.
9
- */
10
- export declare const createCacheManager: (url: string) => Promise<CacheManager>;
11
- export declare function canUseCacheApi(): boolean;
12
- export declare function normalizeUrl(url: string): string;
13
- /**
14
- * Проверяет, не просрочен ли кэшированный ответ
15
- */
16
- export declare function isResponseExpired(response: Response): boolean;
17
- export declare function cloneResponse(response: Response, url: string): Response;
18
- /**
19
- * Извлекает expires из оригинального URL (параметр expires1)
20
- * Предполагается, что expires1 — timestamp в секундах
21
- */
22
- export declare function getExpiresFromUrl(url: string): number;