@companix/utils-js 0.0.1
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/dist/array.d.ts +4 -0
- package/dist/bundle.cjs.js +1 -0
- package/dist/bundle.esm.js +1 -0
- package/dist/bytes.d.ts +1 -0
- package/dist/candle.d.ts +10 -0
- package/dist/colors.d.ts +2 -0
- package/dist/emiters.d.ts +18 -0
- package/dist/http-api.d.ts +14 -0
- package/dist/http-headers.d.ts +12 -0
- package/dist/http-upload.d.ts +6 -0
- package/dist/index.d.ts +17 -0
- package/dist/money.d.ts +7 -0
- package/dist/nouns.d.ts +1 -0
- package/dist/number.d.ts +8 -0
- package/dist/object.d.ts +5 -0
- package/dist/queue.d.ts +6 -0
- package/dist/random.d.ts +10 -0
- package/dist/regex.d.ts +4 -0
- package/dist/time.d.ts +8 -0
- package/dist/utils.d.ts +4 -0
- package/package.json +24 -0
- package/rollup.config.ts +40 -0
- package/src/array.ts +55 -0
- package/src/bytes.ts +13 -0
- package/src/candle.ts +20 -0
- package/src/colors.ts +32 -0
- package/src/emiters.ts +83 -0
- package/src/http-api.ts +43 -0
- package/src/http-headers.ts +34 -0
- package/src/http-upload.ts +23 -0
- package/src/index.ts +17 -0
- package/src/money.ts +31 -0
- package/src/nouns.ts +16 -0
- package/src/number.ts +81 -0
- package/src/object.ts +32 -0
- package/src/queue.ts +38 -0
- package/src/random.ts +49 -0
- package/src/regex.ts +6 -0
- package/src/time.ts +70 -0
- package/src/utils.ts +15 -0
- package/tsconfig.json +20 -0
package/dist/array.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const range: (from: number, to: number, step?: number) => number[];
|
|
2
|
+
export declare const splitByChunks: <T>(items: T[], chunkSize: number) => T[][];
|
|
3
|
+
export declare const separateArray: <T>(array: T[], parts: number) => T[][];
|
|
4
|
+
export declare const roundSeparateArray: <T>(array: T[], size: number) => T[][];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var t=require("axios");const e=(t,e)=>{if(e<=0)throw new Error("Number of parts must be greater than 0");const r=[],s=Math.floor(t.length/e);let o=t.length%e,n=0;for(let i=0;i<e;i++){const e=n+s+(o>0?1:0),i=t.slice(n,e);i.length>0&&r.push(i),n=e,o--}return r};const r=(t,e)=>{const r=Math.pow(10,e);return Math.round(t*r)/r},s=(t,e=1)=>t>0?[0,".",...new Array(t-1).fill(0),e].join(""):`${e}`,o=t=>Object.keys(t);const n=(t,e)=>Math.floor(Math.random()*(e-t+1))+t;var i;!function(t){t.Get="GET",t.Post="POST"}(i||(i={}));const a=(t,e="token")=>{const r=t??localStorage.getItem(e);return r?`Bearer ${r}`:""},u=t=>t.toString().padStart(2,"0"),c={"1s":1e3,"1m":6e4,"3m":18e4,"5m":3e5},p=(t,e)=>Math.floor(t/c[e])*c[e];exports.EventBroadcaster=class{subscribers=[];emit(t){this.subscribers.forEach(e=>{e(t)})}subscribe(t){return this.subscribers.push(t),()=>{this.unsubscribe(t)}}unsubscribe(t){const e=this.subscribers.findIndex(e=>e===t);-1!==e&&this.subscribers.splice(e,1)}},exports.EventDispatcher=class{store={};emit(t,e){this.store[t]&&this.store[t](e)}on(t,e){return this.store[t]=e,()=>{this.rm(t)}}rm(t){delete this.store[t]}},exports.EventEmmiter=class{store={};emit(t,e){this.store[t]&&this.store[t].forEach(t=>t(e))}subscribe(t,e){return this.store[t]||(this.store[t]=[]),this.store[t].push(e),()=>{this.unsubscribe(t,e)}}unsubscribe(t,e){if(this.store[t]){const r=this.store[t].findIndex(t=>t===e);-1!==r&&(this.store[t].splice(r,1),0===this.store[t].length&&delete this.store[t])}}},exports.HttpAPI=class{http;constructor(e){this.http=t.create(e)}async request(t){const{url:e,body:r={},method:s,config:o={}}=t,n=s===i.Get?"params":"data";return this.http({url:e,method:s,[n]:r,...o}).then(({data:t})=>t)}post(t,e,r){return this.request({method:i.Post,url:t,config:r,body:e})}get(t,e,r){return this.request({method:i.Get,url:t,config:r,body:e})}},exports.QueueManager=class{active=!1;queue=[];add(t){return new Promise((e,r)=>{this.queue.push({resolve:e,reject:r,target:t}),this.active||this.next()})}async next(){const t=this.queue[0];t?(this.active=!0,await t.target().then(e=>t.resolve(e)).catch(e=>t.reject(e)).finally(()=>this.queue.shift()),await this.next()):this.active=!1}},exports.applyAlphaToHex=(t,e)=>{t.startsWith("#")&&(t=t.slice(1));return`rgba(${parseInt(t.slice(0,2),16)}, ${parseInt(t.slice(2,4),16)}, ${parseInt(t.slice(4,6),16)}, ${Math.max(0,Math.min(100,e))/100})`},exports.bigMoneyAmount=t=>{const e=t/1e3;if(e>=1e3){const t=e/1e3;return t>=1e3?t/1e3+" МЛРД. ₽":`${t} МЛН. ₽`}return`${e} ТЫС. ₽`},exports.contain=t=>t.reduce((t,e)=>[...t,...e],[]),exports.cutNumber=(t,e)=>{const r=Math.pow(10,e);return Math.floor(t*r)/r},exports.dollars=(t,e)=>t.toLocaleString("us-US",{style:"currency",currency:"USD"}),exports.formatBytes=t=>{const e=t/1024;return e>1024?+(e/1024).toFixed(1)+" MB":e<1?t+" B":Math.trunc(+e.toFixed(2))+" KB"},exports.formatTime=u,exports.generateCode=t=>{const e="A0B1C2D3E4F5G6H7W89K0L1M2N34P5Q6R7S8T9U0V3XYZ",r=[];for(let s=0;s<t;s++){const t=Math.floor(45*Math.random());r.push(e[t])}return r.join("")},exports.generatePreview=t=>{const e=URL.createObjectURL(t);return{url:e,destroy:()=>{URL.revokeObjectURL(e)}}},exports.getAuthToken=a,exports.getCurrentCandleTime=p,exports.getDate=(t,e=!0)=>{const r=new Date(t),s=e?"UTC":"";return[u(r[`get${s}Date`]()),u(r[`get${s}Month`]()+1),u(r[`get${s}FullYear`]())].join(":")},exports.getFiles=t=>Array.from(t),exports.getFloatDigits=t=>{if(t.includes("e")){const[e,r]=t.split("e");return Math.max((e.split(".")[1]?.length||0)-+r,0)}return t.split(".")[1]?.length||0},exports.getHeaders=({token:t,tokenStore:e,multipart:r}={})=>{const s=a(t,e),o={"Content-Type":r?"multipart/form-data; charset=utf-8":"application/json"};return s&&(o.Authorization=s),o},exports.getLastClosedCandleTime=(t,e)=>p(t,e)-c[e],exports.getNextCandleTime=(t,e)=>p(t,e)+c[e],exports.getNoun=(t,e,r,s)=>{let o=Math.abs(t);return o%=100,o>=5&&o<=20?s:(o%=10,1===o?e:o>=2&&o<=4?r:s)},exports.getNum=t=>isNaN(Number(t))?null:+t,exports.getPrecisionStep=s,exports.getRandomInt=n,exports.getRandomIntString=t=>new Array(t).fill(0).map(()=>n(0,9)).join(""),exports.getRandomItemFromArray=(t,e)=>{const r=e&&e.exceptions.length<t.length?t.filter(t=>!e.exceptions.some(r=>e.isEqual(t,r))):t;return r[r.length*Math.random()|0]},exports.getTime=(t,e=!0)=>{const r=new Date(t),s=e?"UTC":"";return[u(r[`get${s}Hours`]()),u(r[`get${s}Minutes`]()),u(r[`get${s}Seconds`]())].join(":")},exports.getTimes=t=>{t<0&&(t=0);const e=Math.floor(t/1e3);return{hours:Math.floor(e/3600),minutes:Math.floor(e%3600/60),seconds:e%60}},exports.hash=()=>{const t="undefined"==typeof performance?Date.now():1e3*performance.now();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const r=(16*Math.random()+t)%16|0;return("x"===e?r:3&r|8).toString(16)})},exports.intervals=c,exports.isEmpty=t=>{for(const e in t)return!1;return!0},exports.isValidPrecision=(t,e)=>{const r=t.toString();return!r.includes(".")||0!==e&&r.split(".")[1].length<=e},exports.keys=o,exports.keysLength=t=>o(t).length,exports.nonNullable=t=>null!=t,exports.normalize=r,exports.omit=(t,e)=>(delete t[e],t),exports.partial=t=>{const e={};for(const r in t)t[r]&&(e[r]=t[r]);return e},exports.range=(t,e,r=1)=>{const s=t<e?1:-1,o=Math.abs(t-e)+1,n=Math.ceil(o/r),i=Array(n);for(let e=0;e<i.length;e++)i[e]=t+e*r*s;return i},exports.regex={email:/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,phone_number:/^[0-9]+$/},exports.rgbaToHex=t=>{const e=t.match(/rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d.]+)\s*\)/);if(!e)throw new Error("Некорректный формат RGBA");const[,r,s,o,n]=e,i=t=>parseInt(t).toString(16).padStart(2,"0"),a=Math.round(255*parseFloat(n));return`#${i(r)}${i(s)}${i(o)}${i(a.toString())}`},exports.roundSeparateArray=(t,r)=>{const s=Math.round(t.length/r);return s>1?e(t,s):[t]},exports.roundUp2=(t,e)=>{const o=t.toFixed(e);return t>+o?r(+o+ +s(e),e):+o},exports.rubles=(t,e)=>t.toLocaleString("ru-RU",{style:"currency",currency:"RUB",...e?.noPennies?{minimumFractionDigits:0,maximumFractionDigits:0}:{}}),exports.separateArray=e,exports.sleep=t=>new Promise(e=>setTimeout(e,t)),exports.splitByChunks=(t,e)=>{const r=[];for(let s=0;s<t.length;s+=e)r.push(t.slice(s,s+e));return r},exports.stringify=(...t)=>t.map(t=>JSON.stringify(t)).join(" "),exports.transport=t=>{const e=new FormData;return e.append("file",t),e.append("type","file"),e},exports.truncateNumber=(t,e)=>{const r=t.toFixed(e);return+r>t?(+r-Math.sign(t)*Math.pow(.1,e)).toFixed(e):r};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"axios";const e=(t,e,r=1)=>{const s=t<e?1:-1,n=Math.abs(t-e)+1,o=Math.ceil(n/r),i=Array(o);for(let e=0;e<i.length;e++)i[e]=t+e*r*s;return i},r=(t,e)=>{const r=[];for(let s=0;s<t.length;s+=e)r.push(t.slice(s,s+e));return r},s=(t,e)=>{if(e<=0)throw new Error("Number of parts must be greater than 0");const r=[],s=Math.floor(t.length/e);let n=t.length%e,o=0;for(let i=0;i<e;i++){const e=o+s+(n>0?1:0),i=t.slice(o,e);i.length>0&&r.push(i),o=e,n--}return r},n=(t,e)=>{const r=Math.round(t.length/e);return r>1?s(t,r):[t]},o=(t,e)=>{t.startsWith("#")&&(t=t.slice(1));return`rgba(${parseInt(t.slice(0,2),16)}, ${parseInt(t.slice(2,4),16)}, ${parseInt(t.slice(4,6),16)}, ${Math.max(0,Math.min(100,e))/100})`},i=t=>{const e=t.match(/rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d.]+)\s*\)/);if(!e)throw new Error("Некорректный формат RGBA");const[,r,s,n,o]=e,i=t=>parseInt(t).toString(16).padStart(2,"0"),a=Math.round(255*parseFloat(o));return`#${i(r)}${i(s)}${i(n)}${i(a.toString())}`};class a{store={};emit(t,e){this.store[t]&&this.store[t](e)}on(t,e){return this.store[t]=e,()=>{this.rm(t)}}rm(t){delete this.store[t]}}class c{store={};emit(t,e){this.store[t]&&this.store[t].forEach(t=>t(e))}subscribe(t,e){return this.store[t]||(this.store[t]=[]),this.store[t].push(e),()=>{this.unsubscribe(t,e)}}unsubscribe(t,e){if(this.store[t]){const r=this.store[t].findIndex(t=>t===e);-1!==r&&(this.store[t].splice(r,1),0===this.store[t].length&&delete this.store[t])}}}class u{subscribers=[];emit(t){this.subscribers.forEach(e=>{e(t)})}subscribe(t){return this.subscribers.push(t),()=>{this.unsubscribe(t)}}unsubscribe(t){const e=this.subscribers.findIndex(e=>e===t);-1!==e&&this.subscribers.splice(e,1)}}const h=t=>{if(t.includes("e")){const[e,r]=t.split("e");return Math.max((e.split(".")[1]?.length||0)-+r,0)}return t.split(".")[1]?.length||0},l=(t,e)=>{const r=Math.pow(10,e);return Math.round(t*r)/r},m=(t,e)=>{const r=Math.pow(10,e);return Math.floor(t*r)/r},p=(t,e)=>{const r=t.toFixed(e);return t>+r?l(+r+ +d(e),e):+r},x=(t,e)=>{const r=t.toFixed(e);return+r>t?(+r-Math.sign(t)*Math.pow(.1,e)).toFixed(e):r},d=(t,e=1)=>t>0?[0,".",...new Array(t-1).fill(0),e].join(""):`${e}`,f=(t,e)=>{const r=t.toString();return!r.includes(".")||0!==e&&r.split(".")[1].length<=e},g=t=>isNaN(Number(t))?null:+t,b=t=>Object.keys(t),M=t=>b(t).length,y=t=>{for(const e in t)return!1;return!0},$=(t,e)=>(delete t[e],t),w=t=>{const e={};for(const r in t)t[r]&&(e[r]=t[r]);return e};class S{active=!1;queue=[];add(t){return new Promise((e,r)=>{this.queue.push({resolve:e,reject:r,target:t}),this.active||this.next()})}async next(){const t=this.queue[0];t?(this.active=!0,await t.target().then(e=>t.resolve(e)).catch(e=>t.reject(e)).finally(()=>this.queue.shift()),await this.next()):this.active=!1}}const j=t=>{const e="A0B1C2D3E4F5G6H7W89K0L1M2N34P5Q6R7S8T9U0V3XYZ",r=[];for(let s=0;s<t;s++){const t=Math.floor(45*Math.random());r.push(e[t])}return r.join("")},F=(t,e)=>Math.floor(Math.random()*(e-t+1))+t,U=t=>new Array(t).fill(0).map(()=>F(0,9)).join(""),A=()=>{const t="undefined"==typeof performance?Date.now():1e3*performance.now();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const r=(16*Math.random()+t)%16|0;return("x"===e?r:3&r|8).toString(16)})},D=(t,e)=>{const r=e&&e.exceptions.length<t.length?t.filter(t=>!e.exceptions.some(r=>e.isEqual(t,r))):t;return r[r.length*Math.random()|0]},q=t=>new Promise(e=>setTimeout(e,t)),v=(...t)=>t.map(t=>JSON.stringify(t)).join(" "),R=t=>t.reduce((t,e)=>[...t,...e],[]),B=t=>null!=t;var E;!function(t){t.Get="GET",t.Post="POST"}(E||(E={}));class I{http;constructor(e){this.http=t.create(e)}async request(t){const{url:e,body:r={},method:s,config:n={}}=t,o=s===E.Get?"params":"data";return this.http({url:e,method:s,[o]:r,...n}).then(({data:t})=>t)}post(t,e,r){return this.request({method:E.Post,url:t,config:r,body:e})}get(t,e,r){return this.request({method:E.Get,url:t,config:r,body:e})}}const L=({token:t,tokenStore:e,multipart:r}={})=>{const s=P(t,e),n={"Content-Type":r?"multipart/form-data; charset=utf-8":"application/json"};return s&&(n.Authorization=s),n},P=(t,e="token")=>{const r=t??localStorage.getItem(e);return r?`Bearer ${r}`:""},T=t=>{const e=new FormData;return e.append("file",t),e.append("type","file"),e},G=t=>{const e=URL.createObjectURL(t);return{url:e,destroy:()=>{URL.revokeObjectURL(e)}}},N=t=>Array.from(t),k=t=>{const e=t/1e3;if(e>=1e3){const t=e/1e3;return t>=1e3?t/1e3+" МЛРД. ₽":`${t} МЛН. ₽`}return`${e} ТЫС. ₽`},O=(t,e)=>t.toLocaleString("ru-RU",{style:"currency",currency:"RUB",...e?.noPennies?{minimumFractionDigits:0,maximumFractionDigits:0}:{}}),C=(t,e)=>t.toLocaleString("us-US",{style:"currency",currency:"USD"}),z=(t,e,r,s)=>{let n=Math.abs(t);return n%=100,n>=5&&n<=20?s:(n%=10,1===n?e:n>=2&&n<=4?r:s)},Z={email:/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,phone_number:/^[0-9]+$/},H=t=>{const e=t/1024;return e>1024?+(e/1024).toFixed(1)+" MB":e<1?t+" B":Math.trunc(+e.toFixed(2))+" KB"},K=t=>t.toString().padStart(2,"0"),W=t=>{t<0&&(t=0);const e=Math.floor(t/1e3);return{hours:Math.floor(e/3600),minutes:Math.floor(e%3600/60),seconds:e%60}},Y=(t,e=!0)=>{const r=new Date(t),s=e?"UTC":"";return[K(r[`get${s}Hours`]()),K(r[`get${s}Minutes`]()),K(r[`get${s}Seconds`]())].join(":")},J=(t,e=!0)=>{const r=new Date(t),s=e?"UTC":"";return[K(r[`get${s}Date`]()),K(r[`get${s}Month`]()+1),K(r[`get${s}FullYear`]())].join(":")},Q={"1s":1e3,"1m":6e4,"3m":18e4,"5m":3e5},V=(t,e)=>X(t,e)-Q[e],X=(t,e)=>Math.floor(t/Q[e])*Q[e],_=(t,e)=>X(t,e)+Q[e];export{u as EventBroadcaster,a as EventDispatcher,c as EventEmmiter,I as HttpAPI,S as QueueManager,o as applyAlphaToHex,k as bigMoneyAmount,R as contain,m as cutNumber,C as dollars,H as formatBytes,K as formatTime,j as generateCode,G as generatePreview,P as getAuthToken,X as getCurrentCandleTime,J as getDate,N as getFiles,h as getFloatDigits,L as getHeaders,V as getLastClosedCandleTime,_ as getNextCandleTime,z as getNoun,g as getNum,d as getPrecisionStep,F as getRandomInt,U as getRandomIntString,D as getRandomItemFromArray,Y as getTime,W as getTimes,A as hash,Q as intervals,y as isEmpty,f as isValidPrecision,b as keys,M as keysLength,B as nonNullable,l as normalize,$ as omit,w as partial,e as range,Z as regex,i as rgbaToHex,n as roundSeparateArray,p as roundUp2,O as rubles,s as separateArray,q as sleep,r as splitByChunks,v as stringify,T as transport,x as truncateNumber};
|
package/dist/bytes.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const formatBytes: (bytes: number) => string;
|
package/dist/candle.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Interval = '1s' | '1m' | '3m' | '5m';
|
|
2
|
+
export declare const intervals: {
|
|
3
|
+
'1s': number;
|
|
4
|
+
'1m': number;
|
|
5
|
+
'3m': number;
|
|
6
|
+
'5m': number;
|
|
7
|
+
};
|
|
8
|
+
export declare const getLastClosedCandleTime: (timestamp: number, interval: Interval) => number;
|
|
9
|
+
export declare const getCurrentCandleTime: (timestamp: number, interval: Interval) => number;
|
|
10
|
+
export declare const getNextCandleTime: (timestamp: number, interval: Interval) => number;
|
package/dist/colors.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare class EventDispatcher<T> {
|
|
2
|
+
private store;
|
|
3
|
+
emit(event: string, data: T): void;
|
|
4
|
+
on(event: string, callback: (data: T) => void): () => void;
|
|
5
|
+
rm(event: string): void;
|
|
6
|
+
}
|
|
7
|
+
export declare class EventEmmiter<T> {
|
|
8
|
+
private store;
|
|
9
|
+
emit(event: string, data: T): void;
|
|
10
|
+
subscribe(event: string, callback: (data: T) => void): () => void;
|
|
11
|
+
unsubscribe(event: string, callback: (data: T) => void): void;
|
|
12
|
+
}
|
|
13
|
+
export declare class EventBroadcaster<T> {
|
|
14
|
+
subscribers: ((data: T) => void)[];
|
|
15
|
+
emit(data: T): void;
|
|
16
|
+
subscribe(callback: (data: T) => void): () => void;
|
|
17
|
+
unsubscribe(callback: (data: T) => void): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults } from 'axios';
|
|
2
|
+
export type IOpattern<T extends IOpattern<T>> = {
|
|
3
|
+
[key in keyof T]: {
|
|
4
|
+
params: T[key]['params'];
|
|
5
|
+
answer: T[key]['answer'];
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
export declare class HttpAPI<T extends IOpattern<T>> {
|
|
9
|
+
http: AxiosInstance;
|
|
10
|
+
constructor(config: CreateAxiosDefaults);
|
|
11
|
+
private request;
|
|
12
|
+
post<K extends keyof T>(url: K, body: T[K]['params'], config?: AxiosRequestConfig): Promise<T[K]["answer"]>;
|
|
13
|
+
get<K extends keyof T>(url: K, body: T[K]['params'], config?: AxiosRequestConfig): Promise<T[K]["answer"]>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Headers {
|
|
2
|
+
Authorization?: string;
|
|
3
|
+
'Content-Type': string;
|
|
4
|
+
}
|
|
5
|
+
interface Options {
|
|
6
|
+
multipart?: boolean;
|
|
7
|
+
token?: string;
|
|
8
|
+
tokenStore?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const getHeaders: ({ token, tokenStore, multipart }?: Options) => Headers;
|
|
11
|
+
export declare const getAuthToken: (token?: string, tokenStore?: string) => string;
|
|
12
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './array';
|
|
2
|
+
export * from './colors';
|
|
3
|
+
export * from './emiters';
|
|
4
|
+
export * from './number';
|
|
5
|
+
export * from './object';
|
|
6
|
+
export * from './queue';
|
|
7
|
+
export * from './random';
|
|
8
|
+
export * from './utils';
|
|
9
|
+
export * from './http-api';
|
|
10
|
+
export * from './http-headers';
|
|
11
|
+
export * from './http-upload';
|
|
12
|
+
export * from './money';
|
|
13
|
+
export * from './nouns';
|
|
14
|
+
export * from './regex';
|
|
15
|
+
export * from './bytes';
|
|
16
|
+
export * from './time';
|
|
17
|
+
export * from './candle';
|
package/dist/money.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const bigMoneyAmount: (amount: number) => string;
|
|
2
|
+
interface CurrencyOptions {
|
|
3
|
+
noPennies?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare const rubles: (num: number, options?: CurrencyOptions) => string;
|
|
6
|
+
export declare const dollars: (value: number, options?: CurrencyOptions) => string;
|
|
7
|
+
export {};
|
package/dist/nouns.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getNoun: (number: number, one: string, two: string, five: string) => string;
|
package/dist/number.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const getFloatDigits: (value: string) => number;
|
|
2
|
+
export declare const normalize: (value: number, precision: number) => number;
|
|
3
|
+
export declare const cutNumber: (value: number, precision: number) => number;
|
|
4
|
+
export declare const roundUp2: (value: number, precision: number) => number;
|
|
5
|
+
export declare const truncateNumber: (value: number, precision: number) => string;
|
|
6
|
+
export declare const getPrecisionStep: (precision: number, digit?: number) => string;
|
|
7
|
+
export declare const isValidPrecision: (value: number, precision: number) => boolean;
|
|
8
|
+
export declare const getNum: (value: string) => number | null;
|
package/dist/object.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const keys: (object: object) => string[];
|
|
2
|
+
export declare const keysLength: (object: object) => number;
|
|
3
|
+
export declare const isEmpty: (obj: object) => boolean;
|
|
4
|
+
export declare const omit: <T extends object>(object: T, key: keyof T) => T;
|
|
5
|
+
export declare const partial: <T extends object>(object: T) => T;
|
package/dist/queue.d.ts
ADDED
package/dist/random.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const generateCode: (size: number) => string;
|
|
2
|
+
export declare const getRandomInt: (min: number, max: number) => number;
|
|
3
|
+
export declare const getRandomIntString: (n: number) => string;
|
|
4
|
+
export declare const hash: () => string;
|
|
5
|
+
interface ArrItemOptions<T> {
|
|
6
|
+
exceptions: T[];
|
|
7
|
+
isEqual: (a: T, b: T) => boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const getRandomItemFromArray: <T>(values: T[], exception?: ArrItemOptions<T>) => T;
|
|
10
|
+
export {};
|
package/dist/regex.d.ts
ADDED
package/dist/time.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const formatTime: (value: number) => string;
|
|
2
|
+
export declare const getTimes: (ms: number) => {
|
|
3
|
+
hours: number;
|
|
4
|
+
minutes: number;
|
|
5
|
+
seconds: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const getTime: (timestamp: number, utc?: boolean) => string;
|
|
8
|
+
export declare const getDate: (timestamp: number, utc?: boolean) => string;
|
package/dist/utils.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@companix/utils-js",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"main": "dist/bundle.cjs.js",
|
|
5
|
+
"module": "dist/bundle.esm.js",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"author": "Pavel Victorov",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "rm -rf dist && rollup --config rollup.config.ts --configPlugin @rollup/plugin-typescript --bundleConfigAsCjs"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"axios": "^1.11.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
16
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
17
|
+
"@rollup/plugin-strip": "^3.0.4",
|
|
18
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
19
|
+
"@rollup/plugin-typescript": "^11.1.6",
|
|
20
|
+
"rollup": "^4.18.1",
|
|
21
|
+
"rollup-plugin-auto-external": "^2.0.0",
|
|
22
|
+
"@types/big.js": "^6.2.2"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/rollup.config.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve'
|
|
2
|
+
import typescript from '@rollup/plugin-typescript'
|
|
3
|
+
import terser from '@rollup/plugin-terser'
|
|
4
|
+
import strip from '@rollup/plugin-strip'
|
|
5
|
+
import autoExternal from 'rollup-plugin-auto-external'
|
|
6
|
+
|
|
7
|
+
export default [
|
|
8
|
+
// EcmaScript Module (esm) build
|
|
9
|
+
{
|
|
10
|
+
input: './src/index.ts',
|
|
11
|
+
output: {
|
|
12
|
+
file: 'dist/bundle.esm.js',
|
|
13
|
+
format: 'esm'
|
|
14
|
+
},
|
|
15
|
+
external: ['react', 'react-dom'],
|
|
16
|
+
plugins: [
|
|
17
|
+
typescript({ tsconfig: './tsconfig.json', noEmitOnError: true }),
|
|
18
|
+
autoExternal(),
|
|
19
|
+
resolve(),
|
|
20
|
+
strip(),
|
|
21
|
+
terser()
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
// CommonJS (cjs) build
|
|
25
|
+
{
|
|
26
|
+
input: './src/index.ts',
|
|
27
|
+
output: {
|
|
28
|
+
file: 'dist/bundle.cjs.js',
|
|
29
|
+
format: 'cjs'
|
|
30
|
+
},
|
|
31
|
+
external: ['react', 'react-dom'],
|
|
32
|
+
plugins: [
|
|
33
|
+
typescript({ tsconfig: './tsconfig.json', noEmitOnError: true }),
|
|
34
|
+
autoExternal(),
|
|
35
|
+
resolve(),
|
|
36
|
+
strip(),
|
|
37
|
+
terser()
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
]
|
package/src/array.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Генерирует массив с диапазоном чисел. Взято с VKUI
|
|
3
|
+
*/
|
|
4
|
+
export const range = (from: number, to: number, step = 1) => {
|
|
5
|
+
const direction = from < to ? 1 : -1
|
|
6
|
+
const distance = Math.abs(from - to) + 1
|
|
7
|
+
const arrayLength = Math.ceil(distance / step)
|
|
8
|
+
|
|
9
|
+
const arr = Array<number>(arrayLength)
|
|
10
|
+
for (let index = 0; index < arr.length; index++) {
|
|
11
|
+
arr[index] = from + index * step * direction
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return arr
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const splitByChunks = <T>(items: T[], chunkSize: number): T[][] => {
|
|
18
|
+
const result: T[][] = []
|
|
19
|
+
|
|
20
|
+
for (let i = 0; i < items.length; i += chunkSize) {
|
|
21
|
+
result.push(items.slice(i, i + chunkSize))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return result
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const separateArray = <T>(array: T[], parts: number): T[][] => {
|
|
28
|
+
if (parts <= 0) throw new Error('Number of parts must be greater than 0')
|
|
29
|
+
|
|
30
|
+
const result: T[][] = []
|
|
31
|
+
const partSize = Math.floor(array.length / parts)
|
|
32
|
+
let remainder = array.length % parts
|
|
33
|
+
let start = 0
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < parts; i++) {
|
|
36
|
+
// Распределяем остаток по первым частям
|
|
37
|
+
const extra = remainder > 0 ? 1 : 0
|
|
38
|
+
const end = start + partSize + extra
|
|
39
|
+
const data = array.slice(start, end)
|
|
40
|
+
|
|
41
|
+
if (data.length > 0) {
|
|
42
|
+
result.push(data)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
start = end
|
|
46
|
+
remainder--
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const roundSeparateArray = <T>(array: T[], size: number): T[][] => {
|
|
53
|
+
const n = Math.round(array.length / size)
|
|
54
|
+
return n > 1 ? separateArray(array, n) : [array]
|
|
55
|
+
}
|
package/src/bytes.ts
ADDED
package/src/candle.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type Interval = '1s' | '1m' | '3m' | '5m'
|
|
2
|
+
|
|
3
|
+
export const intervals = {
|
|
4
|
+
'1s': 1000,
|
|
5
|
+
'1m': 1000 * 60,
|
|
6
|
+
'3m': 1000 * 60 * 3,
|
|
7
|
+
'5m': 1000 * 60 * 5
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getLastClosedCandleTime = (timestamp: number, interval: Interval) => {
|
|
11
|
+
return getCurrentCandleTime(timestamp, interval) - intervals[interval]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getCurrentCandleTime = (timestamp: number, interval: Interval) => {
|
|
15
|
+
return Math.floor(timestamp / intervals[interval]) * intervals[interval]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getNextCandleTime = (timestamp: number, interval: Interval) => {
|
|
19
|
+
return getCurrentCandleTime(timestamp, interval) + intervals[interval]
|
|
20
|
+
}
|
package/src/colors.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const applyAlphaToHex = (hex: string, opacity: number) => {
|
|
2
|
+
if (hex.startsWith('#')) {
|
|
3
|
+
hex = hex.slice(1)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const r = parseInt(hex.slice(0, 2), 16)
|
|
7
|
+
const g = parseInt(hex.slice(2, 4), 16)
|
|
8
|
+
const b = parseInt(hex.slice(4, 6), 16)
|
|
9
|
+
const alpha = Math.max(0, Math.min(100, opacity)) / 100
|
|
10
|
+
|
|
11
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const rgbaToHex = (rgba: string) => {
|
|
15
|
+
// Извлекаем значения с помощью регулярного выражения
|
|
16
|
+
const match = rgba.match(/rgba?\s*\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*([\d.]+)\s*\)/)
|
|
17
|
+
|
|
18
|
+
if (!match) {
|
|
19
|
+
throw new Error('Некорректный формат RGBA')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const [, r, g, b, a] = match
|
|
23
|
+
|
|
24
|
+
const toHex = (n: string) => {
|
|
25
|
+
const hex = parseInt(n).toString(16)
|
|
26
|
+
return hex.padStart(2, '0')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const alpha = Math.round(parseFloat(a) * 255)
|
|
30
|
+
|
|
31
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}${toHex(alpha.toString())}`
|
|
32
|
+
}
|
package/src/emiters.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export class EventDispatcher<T> {
|
|
2
|
+
private store: Record<string, (data: T) => void> = {}
|
|
3
|
+
|
|
4
|
+
emit(event: string, data: T) {
|
|
5
|
+
if (this.store[event]) {
|
|
6
|
+
this.store[event](data)
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
on(event: string, callback: (data: T) => void) {
|
|
11
|
+
this.store[event] = callback
|
|
12
|
+
|
|
13
|
+
return () => {
|
|
14
|
+
this.rm(event)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
rm(event: string) {
|
|
19
|
+
delete this.store[event]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class EventEmmiter<T> {
|
|
24
|
+
private store: Record<string, ((data: T) => void)[]> = {}
|
|
25
|
+
|
|
26
|
+
emit(event: string, data: T) {
|
|
27
|
+
if (this.store[event]) {
|
|
28
|
+
this.store[event].forEach((callback) => callback(data))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
subscribe(event: string, callback: (data: T) => void) {
|
|
33
|
+
if (!this.store[event]) {
|
|
34
|
+
this.store[event] = []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.store[event].push(callback)
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
this.unsubscribe(event, callback)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
unsubscribe(event: string, callback: (data: T) => void) {
|
|
45
|
+
if (this.store[event]) {
|
|
46
|
+
const index = this.store[event].findIndex((u) => u === callback)
|
|
47
|
+
|
|
48
|
+
if (index !== -1) {
|
|
49
|
+
this.store[event].splice(index, 1)
|
|
50
|
+
|
|
51
|
+
if (this.store[event].length === 0) {
|
|
52
|
+
delete this.store[event]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class EventBroadcaster<T> {
|
|
60
|
+
public subscribers: ((data: T) => void)[] = []
|
|
61
|
+
|
|
62
|
+
emit(data: T) {
|
|
63
|
+
this.subscribers.forEach((callback) => {
|
|
64
|
+
callback(data)
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
subscribe(callback: (data: T) => void) {
|
|
69
|
+
this.subscribers.push(callback)
|
|
70
|
+
|
|
71
|
+
return () => {
|
|
72
|
+
this.unsubscribe(callback)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
unsubscribe(callback: (data: T) => void) {
|
|
77
|
+
const index = this.subscribers.findIndex((u) => u === callback)
|
|
78
|
+
|
|
79
|
+
if (index !== -1) {
|
|
80
|
+
this.subscribers.splice(index, 1)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
package/src/http-api.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import axios, { AxiosInstance, AxiosRequestConfig, CreateAxiosDefaults } from 'axios'
|
|
2
|
+
|
|
3
|
+
export type IOpattern<T extends IOpattern<T>> = {
|
|
4
|
+
[key in keyof T]: {
|
|
5
|
+
params: T[key]['params']
|
|
6
|
+
answer: T[key]['answer']
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
enum HttpVerb {
|
|
11
|
+
Get = 'GET',
|
|
12
|
+
Post = 'POST'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface RequestInterface {
|
|
16
|
+
url: string
|
|
17
|
+
body?: any
|
|
18
|
+
config?: AxiosRequestConfig
|
|
19
|
+
method: HttpVerb
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class HttpAPI<T extends IOpattern<T>> {
|
|
23
|
+
public http: AxiosInstance
|
|
24
|
+
|
|
25
|
+
constructor(config: CreateAxiosDefaults) {
|
|
26
|
+
this.http = axios.create(config)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private async request<T>(props: RequestInterface): Promise<T> {
|
|
30
|
+
const { url, body = {}, method, config = {} } = props
|
|
31
|
+
const dataAtt = method === HttpVerb.Get ? 'params' : 'data'
|
|
32
|
+
|
|
33
|
+
return this.http({ url, method, [dataAtt]: body, ...config }).then(({ data }) => data)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
post<K extends keyof T>(url: K, body: T[K]['params'], config?: AxiosRequestConfig) {
|
|
37
|
+
return this.request<T[K]['answer']>({ method: HttpVerb.Post, url: url as string, config, body })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get<K extends keyof T>(url: K, body: T[K]['params'], config?: AxiosRequestConfig) {
|
|
41
|
+
return this.request<T[K]['answer']>({ method: HttpVerb.Get, url: url as string, config, body })
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
interface Headers {
|
|
2
|
+
Authorization?: string
|
|
3
|
+
'Content-Type': string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface Options {
|
|
7
|
+
multipart?: boolean
|
|
8
|
+
token?: string
|
|
9
|
+
tokenStore?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getHeaders = ({ token, tokenStore, multipart }: Options = {}): Headers => {
|
|
13
|
+
const Authorization = getAuthToken(token, tokenStore)
|
|
14
|
+
|
|
15
|
+
const headers: Headers = {
|
|
16
|
+
'Content-Type': multipart ? 'multipart/form-data; charset=utf-8' : 'application/json'
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (Authorization) {
|
|
20
|
+
headers.Authorization = Authorization
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return headers
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const getAuthToken = (token?: string, tokenStore: string = 'token') => {
|
|
27
|
+
const value = token ?? localStorage.getItem(tokenStore)
|
|
28
|
+
|
|
29
|
+
if (value) {
|
|
30
|
+
return `Bearer ${value}`
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return ''
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const transport = (file: Blob) => {
|
|
2
|
+
const data = new FormData()
|
|
3
|
+
|
|
4
|
+
data.append('file', file)
|
|
5
|
+
data.append('type', 'file')
|
|
6
|
+
|
|
7
|
+
return data
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const generatePreview = (file: File) => {
|
|
11
|
+
const url = URL.createObjectURL(file)
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
url,
|
|
15
|
+
destroy: () => {
|
|
16
|
+
URL.revokeObjectURL(url)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const getFiles = (files: FileList) => {
|
|
22
|
+
return Array.from(files)
|
|
23
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './array'
|
|
2
|
+
export * from './colors'
|
|
3
|
+
export * from './emiters'
|
|
4
|
+
export * from './number'
|
|
5
|
+
export * from './object'
|
|
6
|
+
export * from './queue'
|
|
7
|
+
export * from './random'
|
|
8
|
+
export * from './utils'
|
|
9
|
+
export * from './http-api'
|
|
10
|
+
export * from './http-headers'
|
|
11
|
+
export * from './http-upload'
|
|
12
|
+
export * from './money'
|
|
13
|
+
export * from './nouns'
|
|
14
|
+
export * from './regex'
|
|
15
|
+
export * from './bytes'
|
|
16
|
+
export * from './time'
|
|
17
|
+
export * from './candle'
|
package/src/money.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const bigMoneyAmount = (amount: number) => {
|
|
2
|
+
const thousand = amount / 1000
|
|
3
|
+
|
|
4
|
+
if (thousand >= 1000) {
|
|
5
|
+
const million = thousand / 1000
|
|
6
|
+
|
|
7
|
+
if (million >= 1000) {
|
|
8
|
+
return `${million / 1000} МЛРД. ₽`
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return `${million} МЛН. ₽`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return `${thousand} ТЫС. ₽`
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface CurrencyOptions {
|
|
18
|
+
noPennies?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const rubles = (num: number, options?: CurrencyOptions) => {
|
|
22
|
+
return num.toLocaleString('ru-RU', {
|
|
23
|
+
style: 'currency',
|
|
24
|
+
currency: 'RUB',
|
|
25
|
+
...(options?.noPennies ? { minimumFractionDigits: 0, maximumFractionDigits: 0 } : {})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const dollars = (value: number, options?: CurrencyOptions) => {
|
|
30
|
+
return value.toLocaleString('us-US', { style: 'currency', currency: 'USD' })
|
|
31
|
+
}
|
package/src/nouns.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// https://gist.github.com/tomfun/830fa6d8030d16007bbab50a5b21ef97
|
|
2
|
+
export const getNoun = (number: number, one: string, two: string, five: string) => {
|
|
3
|
+
let n = Math.abs(number)
|
|
4
|
+
n %= 100
|
|
5
|
+
if (n >= 5 && n <= 20) {
|
|
6
|
+
return five
|
|
7
|
+
}
|
|
8
|
+
n %= 10
|
|
9
|
+
if (n === 1) {
|
|
10
|
+
return one
|
|
11
|
+
}
|
|
12
|
+
if (n >= 2 && n <= 4) {
|
|
13
|
+
return two
|
|
14
|
+
}
|
|
15
|
+
return five
|
|
16
|
+
}
|
package/src/number.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export const getFloatDigits = (value: string) => {
|
|
2
|
+
if (value.includes('e')) {
|
|
3
|
+
const [int, exp] = value.split('e')
|
|
4
|
+
return Math.max((int.split('.')[1]?.length || 0) - +exp, 0)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return value.split('.')[1]?.length || 0
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* не используйте normalize для округления, когда требуемая точность меньше точности входного значения
|
|
12
|
+
*
|
|
13
|
+
* ❌ normalize(0.000020025, 8) -> 0.00002002
|
|
14
|
+
* ❌ normalize(0.000022025, 8) -> 0.00002203 (!)
|
|
15
|
+
*
|
|
16
|
+
* использование normalize безопасно в случаях нормализации значения: normalize(a + b, precision), если a и b с точностью precision
|
|
17
|
+
*
|
|
18
|
+
* ✅ normalize(0.000020025, 9) -> 0.000020025
|
|
19
|
+
* ✅ normalize(0.000022025, 9) -> 0.000022025
|
|
20
|
+
*
|
|
21
|
+
* 0.1 + 0.2 -> 0.30000000000000004 (10^1) -> 3.0000000000000004 -> (round) 3
|
|
22
|
+
* 0.0001 + 0.0003 -> 0.00039999999999999996 -> (10^4) -> 3.9999999999999996 -> (round) 4
|
|
23
|
+
*
|
|
24
|
+
* работает корректно до precision <= 22. После: 123 / (10 ** 23) -> 1.2300000000000002e-21
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export const normalize = (value: number, precision: number) => {
|
|
28
|
+
const factor = Math.pow(10, precision)
|
|
29
|
+
return Math.round(value * factor) / factor // 0.000020025 * 10^8 -> 2002.4999999999998
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const cutNumber = (value: number, precision: number) => {
|
|
33
|
+
const factor = Math.pow(10, precision)
|
|
34
|
+
return Math.floor(value * factor) / factor
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const roundUp2 = (value: number, precision: number) => {
|
|
38
|
+
const result = value.toFixed(precision)
|
|
39
|
+
|
|
40
|
+
if (value > +result) {
|
|
41
|
+
return normalize(+result + +getPrecisionStep(precision), precision)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return +result
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const truncateNumber = (value: number, precision: number) => {
|
|
48
|
+
const result = value.toFixed(precision)
|
|
49
|
+
|
|
50
|
+
if (+result > value) {
|
|
51
|
+
return (+result - Math.sign(value) * Math.pow(0.1, precision)).toFixed(precision)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getPrecisionStep = (precision: number, digit: number = 1) => {
|
|
58
|
+
if (precision > 0) {
|
|
59
|
+
return [0, '.', ...new Array(precision - 1).fill(0), digit].join('')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return `${digit}`
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const isValidPrecision = (value: number, precision: number) => {
|
|
66
|
+
const num = value.toString()
|
|
67
|
+
|
|
68
|
+
if (num.includes('.')) {
|
|
69
|
+
if (precision === 0) {
|
|
70
|
+
return false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return num.split('.')[1].length <= precision
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return true
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const getNum = (value: string) => {
|
|
80
|
+
return isNaN(Number(value)) ? null : +value
|
|
81
|
+
}
|
package/src/object.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const keys = (object: object) => {
|
|
2
|
+
return Object.keys(object)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const keysLength = (object: object) => {
|
|
6
|
+
return keys(object).length
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const isEmpty = (obj: object) => {
|
|
10
|
+
for (const _ in obj) {
|
|
11
|
+
return false
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const omit = <T extends object>(object: T, key: keyof T) => {
|
|
18
|
+
delete object[key]
|
|
19
|
+
return object
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const partial = <T extends object>(object: T): T => {
|
|
23
|
+
const response = {} as T
|
|
24
|
+
|
|
25
|
+
for (const key in object) {
|
|
26
|
+
if (object[key]) {
|
|
27
|
+
response[key] = object[key]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return response
|
|
32
|
+
}
|
package/src/queue.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
interface Subject {
|
|
2
|
+
resolve: (value: any) => void
|
|
3
|
+
reject: (reason?: any) => void
|
|
4
|
+
target: () => Promise<any>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class QueueManager {
|
|
8
|
+
private active: boolean = false
|
|
9
|
+
private queue: Subject[] = []
|
|
10
|
+
|
|
11
|
+
add<T>(target: () => Promise<T>): Promise<T> {
|
|
12
|
+
return new Promise<T>((resolve, reject) => {
|
|
13
|
+
this.queue.push({ resolve, reject, target })
|
|
14
|
+
|
|
15
|
+
if (!this.active) {
|
|
16
|
+
this.next()
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private async next() {
|
|
22
|
+
const subject = this.queue[0]
|
|
23
|
+
|
|
24
|
+
if (subject) {
|
|
25
|
+
this.active = true
|
|
26
|
+
|
|
27
|
+
await subject
|
|
28
|
+
.target()
|
|
29
|
+
.then((data) => subject.resolve(data))
|
|
30
|
+
.catch((e) => subject.reject(e))
|
|
31
|
+
.finally(() => this.queue.shift())
|
|
32
|
+
|
|
33
|
+
await this.next()
|
|
34
|
+
} else {
|
|
35
|
+
this.active = false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/random.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export const generateCode = (size: number) => {
|
|
2
|
+
const characters = 'A0B1C2D3E4F5G6H7W89K0L1M2N34P5Q6R7S8T9U0V3XYZ' // 33 uniq symbols
|
|
3
|
+
const result: string[] = []
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < size; i++) {
|
|
6
|
+
const randomIndex = Math.floor(Math.random() * characters.length)
|
|
7
|
+
result.push(characters[randomIndex])
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return result.join('')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const getRandomInt = (min: number, max: number) => {
|
|
14
|
+
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const getRandomIntString = (n: number) => {
|
|
18
|
+
return new Array(n)
|
|
19
|
+
.fill(0)
|
|
20
|
+
.map(() => getRandomInt(0, 9))
|
|
21
|
+
.join('')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const hash = () => {
|
|
25
|
+
const d = typeof performance === 'undefined' ? Date.now() : performance.now() * 1000
|
|
26
|
+
|
|
27
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
28
|
+
const r = (Math.random() * 16 + d) % 16 | 0
|
|
29
|
+
|
|
30
|
+
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface ArrItemOptions<T> {
|
|
35
|
+
exceptions: T[]
|
|
36
|
+
isEqual: (a: T, b: T) => boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const getRandomItemFromArray = <T>(values: T[], exception?: ArrItemOptions<T>) => {
|
|
40
|
+
const items = (() => {
|
|
41
|
+
if (exception && exception.exceptions.length < values.length) {
|
|
42
|
+
return values.filter((a) => !exception.exceptions.some((b) => exception.isEqual(a, b)))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return values
|
|
46
|
+
})()
|
|
47
|
+
|
|
48
|
+
return items[(items.length * Math.random()) << 0] as T
|
|
49
|
+
}
|
package/src/regex.ts
ADDED
package/src/time.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export const formatTime = (value: number) => {
|
|
2
|
+
return value.toString().padStart(2, '0')
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const getTimes = (ms: number) => {
|
|
6
|
+
if (ms < 0) {
|
|
7
|
+
ms = 0
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const totalSeconds = Math.floor(ms / 1000)
|
|
11
|
+
|
|
12
|
+
const hours = Math.floor(totalSeconds / 3600)
|
|
13
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
|
14
|
+
const seconds = totalSeconds % 60
|
|
15
|
+
|
|
16
|
+
return { hours, minutes, seconds }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const getTime = (timestamp: number, utc: boolean = true) => {
|
|
20
|
+
const date = new Date(timestamp)
|
|
21
|
+
const prefix = utc ? 'UTC' : ''
|
|
22
|
+
|
|
23
|
+
const times = [
|
|
24
|
+
formatTime(date[`get${prefix}Hours`]()),
|
|
25
|
+
formatTime(date[`get${prefix}Minutes`]()),
|
|
26
|
+
formatTime(date[`get${prefix}Seconds`]())
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
return times.join(':')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const getDate = (timestamp: number, utc: boolean = true) => {
|
|
33
|
+
const date = new Date(timestamp)
|
|
34
|
+
const prefix = utc ? 'UTC' : ''
|
|
35
|
+
|
|
36
|
+
const times = [
|
|
37
|
+
formatTime(date[`get${prefix}Date`]()),
|
|
38
|
+
formatTime(date[`get${prefix}Month`]() + 1),
|
|
39
|
+
formatTime(date[`get${prefix}FullYear`]())
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
return times.join(':')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// export const timeDuration = (ms: number) => {
|
|
46
|
+
// const hours = Math.floor(ms / (1000 * 60 * 60))
|
|
47
|
+
// const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60))
|
|
48
|
+
// const seconds = Math.floor((ms % (1000 * 60)) / 1000)
|
|
49
|
+
// const milliseconds = ms % 1000
|
|
50
|
+
|
|
51
|
+
// const parts: string[] = []
|
|
52
|
+
|
|
53
|
+
// if (hours > 0) {
|
|
54
|
+
// parts.push(`${hours} h.`)
|
|
55
|
+
// }
|
|
56
|
+
|
|
57
|
+
// if (minutes > 0) {
|
|
58
|
+
// parts.push(`${minutes} m.`)
|
|
59
|
+
// }
|
|
60
|
+
|
|
61
|
+
// if (parts.length !== 2 && seconds > 0) {
|
|
62
|
+
// parts.push(`${seconds} s.`)
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
// if (parts.length === 0) {
|
|
66
|
+
// parts.push(`${milliseconds} ms`)
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
// return parts.join(' ')
|
|
70
|
+
// }
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const sleep = (time: number) => {
|
|
2
|
+
return new Promise<void>((resolve) => setTimeout(resolve, time))
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export const stringify = (...ars: any[]) => {
|
|
6
|
+
return ars.map((value) => JSON.stringify(value)).join(' ')
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const contain = <T>(items: T[][]): T[] => {
|
|
10
|
+
return items.reduce((prev, curr) => [...prev, ...curr], [])
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const nonNullable = <T>(value: T): value is NonNullable<T> => {
|
|
14
|
+
return value !== null && value !== undefined
|
|
15
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"strictPropertyInitialization": false,
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"removeComments": true,
|
|
8
|
+
"allowSyntheticDefaultImports": true,
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": false,
|
|
13
|
+
"noFallthroughCasesInSwitch": false,
|
|
14
|
+
"moduleResolution": "node",
|
|
15
|
+
"outDir": "./dist",
|
|
16
|
+
"baseUrl": "./"
|
|
17
|
+
},
|
|
18
|
+
"include": ["./src"],
|
|
19
|
+
"exclude": ["node_modules", "dist"]
|
|
20
|
+
}
|