snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
4
apps/meteor/lib/utils/addMinutesToADate.ts
Normal file
4
apps/meteor/lib/utils/addMinutesToADate.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const addMinutesToADate = (date: Date, minutes: number): Date => {
|
||||
const minutesInMs = minutes * 60 * 1000;
|
||||
return new Date(date.getTime() + minutesInMs);
|
||||
};
|
||||
4
apps/meteor/lib/utils/arrayUtils.ts
Normal file
4
apps/meteor/lib/utils/arrayUtils.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function ensureArray<T>(param: T | Array<T>): Array<T> {
|
||||
const emptyArray: Array<T> = [];
|
||||
return emptyArray.concat(param);
|
||||
}
|
||||
7
apps/meteor/lib/utils/closeUnclosedCodeBlock.ts
Normal file
7
apps/meteor/lib/utils/closeUnclosedCodeBlock.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const closeUnclosedCodeBlock = (text: string): string => {
|
||||
const backtickCount = (text.match(/```/g) || []).length;
|
||||
if (backtickCount % 2 !== 0) {
|
||||
return `${text}\n\`\`\``;
|
||||
}
|
||||
return text;
|
||||
};
|
||||
5
apps/meteor/lib/utils/comparisons.ts
Normal file
5
apps/meteor/lib/utils/comparisons.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const compareByRanking =
|
||||
<T>(rank: (x: T) => number) =>
|
||||
(a: T, b: T) => {
|
||||
return rank(a) - rank(b);
|
||||
};
|
||||
3
apps/meteor/lib/utils/exhaustiveCheck.ts
Normal file
3
apps/meteor/lib/utils/exhaustiveCheck.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const exhaustiveCheck = (_: never): never => {
|
||||
throw new Error('Exhaustive check failed');
|
||||
};
|
||||
13
apps/meteor/lib/utils/generatePath.ts
Normal file
13
apps/meteor/lib/utils/generatePath.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { compile } from 'path-to-regexp';
|
||||
|
||||
type PathParams<TPath extends string> = TPath extends `${string}:${infer TParam}/${infer TRest}`
|
||||
? (TParam extends `${infer U}?` ? U : TParam) | PathParams<TRest>
|
||||
: TPath extends `${string}:${infer TParam}`
|
||||
? TParam extends `${infer U}?`
|
||||
? U
|
||||
: TParam
|
||||
: never;
|
||||
|
||||
export function generatePath<TPath extends string>(path: TPath, params?: Partial<Record<PathParams<TPath>, string>>): string {
|
||||
return compile(path, { encode: encodeURIComponent })(params);
|
||||
}
|
||||
13
apps/meteor/lib/utils/getFileExtension.ts
Normal file
13
apps/meteor/lib/utils/getFileExtension.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const getFileExtension = (fileName?: string): string => {
|
||||
if (!fileName) {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
const arr = fileName.split('.');
|
||||
|
||||
if (arr.length < 2 || (arr[0] === '' && arr.length === 2)) {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
return arr.pop()?.toLocaleUpperCase() || 'file';
|
||||
};
|
||||
69
apps/meteor/lib/utils/highOrderFunctions.ts
Normal file
69
apps/meteor/lib/utils/highOrderFunctions.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
export const withThrottling =
|
||||
({ wait, leading, trailing }: { wait: number; leading?: boolean; trailing?: boolean }) =>
|
||||
<TFunction extends (...args: any[]) => any>(fn: TFunction) => {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
let result: ReturnType<TFunction>;
|
||||
let previous = 0;
|
||||
|
||||
function throttled(this: ThisParameterType<TFunction>, ...args: Parameters<TFunction>) {
|
||||
const now = Date.now();
|
||||
if (!previous && leading === false) previous = now;
|
||||
const remaining = wait - (now - previous);
|
||||
|
||||
if (remaining <= 0 || remaining > wait) {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = undefined;
|
||||
previous = now;
|
||||
result = fn.apply(this, args);
|
||||
} else if (!timer && trailing !== false) {
|
||||
const later = () => {
|
||||
previous = leading === false ? 0 : Date.now();
|
||||
result = fn.apply(this, args);
|
||||
timer = undefined;
|
||||
};
|
||||
timer = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
previous = 0;
|
||||
timer = undefined;
|
||||
};
|
||||
|
||||
return Object.assign(throttled, { cancel });
|
||||
};
|
||||
|
||||
export const withDebouncing =
|
||||
({ wait, immediate }: { wait: number; immediate?: boolean }) =>
|
||||
<TFunction extends (...args: any[]) => any>(fn: TFunction) => {
|
||||
let timer: ReturnType<typeof setTimeout> | undefined = undefined;
|
||||
let result: ReturnType<TFunction>;
|
||||
let previous: number;
|
||||
|
||||
function debounced(this: ThisParameterType<TFunction>, ...args: Parameters<TFunction>) {
|
||||
previous = Date.now();
|
||||
if (!timer) {
|
||||
const later = () => {
|
||||
const passed = Date.now() - previous;
|
||||
if (wait > passed) {
|
||||
timer = setTimeout(later, wait - passed);
|
||||
} else {
|
||||
timer = undefined;
|
||||
if (!immediate) result = fn.apply(this, args);
|
||||
}
|
||||
};
|
||||
timer = setTimeout(later, wait);
|
||||
if (immediate) result = fn.apply(this, args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
clearTimeout(timer);
|
||||
timer = undefined;
|
||||
};
|
||||
|
||||
return Object.assign(debounced, { cancel });
|
||||
};
|
||||
25
apps/meteor/lib/utils/isJSON.spec.ts
Normal file
25
apps/meteor/lib/utils/isJSON.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { isJSON } from './isJSON';
|
||||
|
||||
describe('isJSON', () => {
|
||||
it('returns true for any successfully parsed JSON', () => {
|
||||
expect(isJSON('{"a":1}')).toBe(true);
|
||||
expect(isJSON('{}')).toBe(true);
|
||||
expect(isJSON('[1,2]')).toBe(true);
|
||||
expect(isJSON('"hello"')).toBe(true);
|
||||
expect(isJSON('true')).toBe(true);
|
||||
expect(isJSON('42')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for valid but falsy JSON values', () => {
|
||||
expect(isJSON('false')).toBe(true);
|
||||
expect(isJSON('null')).toBe(true);
|
||||
expect(isJSON('0')).toBe(true);
|
||||
expect(isJSON('""')).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for malformed JSON', () => {
|
||||
expect(isJSON('{ not json }')).toBe(false);
|
||||
expect(isJSON('{"a":')).toBe(false);
|
||||
expect(isJSON('')).toBe(false);
|
||||
});
|
||||
});
|
||||
8
apps/meteor/lib/utils/isJSON.ts
Normal file
8
apps/meteor/lib/utils/isJSON.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const isJSON = (value: string): boolean => {
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
7
apps/meteor/lib/utils/isObject.ts
Normal file
7
apps/meteor/lib/utils/isObject.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @deprecated use isObject from tools package instead
|
||||
*/
|
||||
export function isObject(obj: unknown): obj is object {
|
||||
const type = typeof obj;
|
||||
return type === 'function' || (type === 'object' && !!obj);
|
||||
}
|
||||
3
apps/meteor/lib/utils/isPlainObject.ts
Normal file
3
apps/meteor/lib/utils/isPlainObject.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isPlainObject(value: unknown): value is Record<string | symbol, unknown> {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
1
apps/meteor/lib/utils/isRelativeURL.ts
Normal file
1
apps/meteor/lib/utils/isRelativeURL.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const isRelativeURL = (str: string): boolean => /^[^\/]+\/[^\/].*$|^\/[^\/].*$/gim.test(str);
|
||||
8
apps/meteor/lib/utils/normalizeUsername.ts
Normal file
8
apps/meteor/lib/utils/normalizeUsername.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Strips a leading '@' from a username if present.
|
||||
*
|
||||
* Federated usernames (e.g. @john.doe:matrix.org) are stored with a leading '@',
|
||||
* while local usernames are not. This utility normalises both forms so callers
|
||||
* that render `@{username}` don't accidentally produce `@@john.doe:matrix.org`.
|
||||
*/
|
||||
export const normalizeUsername = (username: string): string => (username.startsWith('@') ? username.slice(1) : username);
|
||||
7
apps/meteor/lib/utils/omit.ts
Normal file
7
apps/meteor/lib/utils/omit.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export function omit<TObject, TKey extends keyof TObject>(obj: TObject, ...keys: TKey[]): Omit<TObject, TKey> {
|
||||
const result = { ...obj };
|
||||
keys.forEach((key) => {
|
||||
delete result[key];
|
||||
});
|
||||
return result;
|
||||
}
|
||||
9
apps/meteor/lib/utils/parseCSV.ts
Normal file
9
apps/meteor/lib/utils/parseCSV.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function parseCSV(csv: string, removeEmptyItems = true): string[] {
|
||||
const items = csv.split(',').map((item) => item.trim());
|
||||
|
||||
if (removeEmptyItems) {
|
||||
return items.filter(Boolean);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
64
apps/meteor/lib/utils/parseParameters.ts
Normal file
64
apps/meteor/lib/utils/parseParameters.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
function split(parameters: string, ignoreExtraUnquotedSpaces = true): string[] {
|
||||
// Replace \n\r with a single \n
|
||||
const line = parameters.replace(/\n\r/gm, '\n');
|
||||
|
||||
const items = line.split(/[ \n\r\t]/);
|
||||
|
||||
if (ignoreExtraUnquotedSpaces) {
|
||||
return items.filter(Boolean);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export function parseParameters(parameters: string, ignoreExtraUnquotedSpaces = true): string[] {
|
||||
if (!parameters.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const match = parameters.match(/((["'])(?:(?=(\\?))\3.)*?\2)/gs);
|
||||
let line = parameters;
|
||||
|
||||
if (!match) {
|
||||
return split(line, ignoreExtraUnquotedSpaces);
|
||||
}
|
||||
|
||||
match.forEach((item) => {
|
||||
const newItem = item
|
||||
// Replace start quote with SSA character
|
||||
.replace(/(^['"])/g, '\u0086')
|
||||
// Replace end quote with ESA character
|
||||
.replace(/['"]$/g, '\u0087')
|
||||
// Replace spaces inside the quotes with a Punctuation Space character
|
||||
.replace(/ /g, '\u2008')
|
||||
// Replace new lines inside the quotes with a PLD character
|
||||
.replace(/\n/gm, '\u008B')
|
||||
// Replace carriage returns inside the quotes with a PLU character
|
||||
.replace(/\r/gm, '\u008C')
|
||||
// Replace tabs inside the quotes with a VTS character
|
||||
.replace(/\t/g, '\u008A');
|
||||
|
||||
line = line.replace(item, newItem);
|
||||
});
|
||||
|
||||
// If two quoted parameters are not separated by a space, add one automatically
|
||||
line = line.replace(/\u0087\u0086/g, '\u0087 \u0086');
|
||||
|
||||
const items = split(line, ignoreExtraUnquotedSpaces);
|
||||
|
||||
return items.map((item) =>
|
||||
item
|
||||
// Convert back the spaces from inside quotes
|
||||
.replace(/\u2008/g, ' ')
|
||||
// Convert back the new lines from inside quotes
|
||||
.replace(/\u008B/g, '\n')
|
||||
// Convert back the carriage returns from inside quotes
|
||||
.replace(/\u008C/g, '\r')
|
||||
// Convert back the tabs from inside quotes
|
||||
.replace(/\u008A/g, '\t')
|
||||
// Remove SSA and ESA characters
|
||||
.replace(/[\u0086\u0087]/g, '')
|
||||
// Unescape quotes
|
||||
.replace(/\\\"/g, '"'),
|
||||
);
|
||||
}
|
||||
5
apps/meteor/lib/utils/safeHtmlDots.ts
Normal file
5
apps/meteor/lib/utils/safeHtmlDots.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// default email engines - like gmail - will render texts with dots as an anchor tag.
|
||||
// If we can, we should avoid that.
|
||||
export const safeHtmlDots = (text: string): string => {
|
||||
return text.replace(/\./g, '․');
|
||||
};
|
||||
14
apps/meteor/lib/utils/secondsToHHMMSS.ts
Normal file
14
apps/meteor/lib/utils/secondsToHHMMSS.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* @todo check alternatives for date/time formatting
|
||||
*/
|
||||
export const secondsToHHMMSS = (sec: number | string): string => {
|
||||
if (typeof sec !== 'number') {
|
||||
sec = parseFloat(sec);
|
||||
}
|
||||
|
||||
const hours = Math.floor(sec / 3600);
|
||||
const minutes = Math.floor((sec - hours * 3600) / 60);
|
||||
const seconds = Math.round(sec - hours * 3600 - minutes * 60);
|
||||
|
||||
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
};
|
||||
2
apps/meteor/lib/utils/sleep.ts
Normal file
2
apps/meteor/lib/utils/sleep.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
// Promisified sleep function
|
||||
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
113
apps/meteor/lib/utils/stringUtils.ts
Normal file
113
apps/meteor/lib/utils/stringUtils.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { escapeRegExp } from '@rocket.chat/tools';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
export function truncate(str: string, length: number): string {
|
||||
return str.length > length ? `${str.slice(0, length - 3)}...` : str;
|
||||
}
|
||||
|
||||
function makeString(object: unknown): string {
|
||||
if (!object) return '';
|
||||
return String(object);
|
||||
}
|
||||
|
||||
function defaultToWhiteSpace(characters: unknown): string {
|
||||
if (!characters) return '\\s';
|
||||
if (typeof characters === 'object' && 'source' in characters) return (characters as { source: string }).source;
|
||||
return `[${escapeRegExp(makeString(characters))}]`;
|
||||
}
|
||||
|
||||
const nativeTrim = String.prototype.trim;
|
||||
const nativeTrimLeft = String.prototype.trimLeft;
|
||||
const nativeTrimRight = String.prototype.trimRight;
|
||||
|
||||
export function trim(_str: unknown, _characters?: unknown): string {
|
||||
const str = makeString(_str);
|
||||
if (!_characters && nativeTrim) return nativeTrim.call(str);
|
||||
const characters = defaultToWhiteSpace(_characters);
|
||||
return str.replace(new RegExp(`^${characters}+|${characters}+$`, 'g'), '');
|
||||
}
|
||||
|
||||
export function ltrim(_str: unknown, _characters: unknown): string {
|
||||
const str = makeString(_str);
|
||||
if (!_characters && nativeTrimLeft) return nativeTrimLeft.call(str);
|
||||
const characters = defaultToWhiteSpace(_characters);
|
||||
return str.replace(new RegExp(`^${characters}+`), '');
|
||||
}
|
||||
|
||||
export function rtrim(_str: unknown, _characters: unknown): string {
|
||||
const str = makeString(_str);
|
||||
if (!_characters && nativeTrimRight) return nativeTrimRight.call(str);
|
||||
const characters = defaultToWhiteSpace(_characters);
|
||||
return str.replace(new RegExp(`${characters}+$`), '');
|
||||
}
|
||||
|
||||
export function capitalize(_str: unknown, lowercaseRest: boolean): string {
|
||||
const str = makeString(_str);
|
||||
const remainingChars = !lowercaseRest ? str.slice(1) : str.slice(1).toLowerCase();
|
||||
|
||||
return str.charAt(0).toUpperCase() + remainingChars;
|
||||
}
|
||||
|
||||
export function stripTags(str: unknown): string {
|
||||
return DOMPurify.sanitize(makeString(str)).replace(/<\/?[^>]+>/g, '');
|
||||
}
|
||||
|
||||
export function strLeft(_str: unknown, _sep: unknown): string {
|
||||
const str = makeString(_str);
|
||||
const sep = makeString(_sep);
|
||||
const pos = !sep ? -1 : str.indexOf(sep);
|
||||
return ~pos ? str.slice(0, pos) : str;
|
||||
}
|
||||
|
||||
export function strRight(_str: unknown, _sep: unknown): string {
|
||||
const str = makeString(_str);
|
||||
const sep = makeString(_sep);
|
||||
const pos = !sep ? -1 : str.indexOf(sep);
|
||||
return ~pos ? str.slice(pos + sep.length, str.length) : str;
|
||||
}
|
||||
|
||||
export function strRightBack(_str: unknown, _sep: unknown): string {
|
||||
const str = makeString(_str);
|
||||
const sep = makeString(_sep);
|
||||
const pos = !sep ? -1 : str.lastIndexOf(sep);
|
||||
return ~pos ? str.slice(pos + sep.length, str.length) : str;
|
||||
}
|
||||
|
||||
export function numberFormat(_number: number, dec: number, dsep?: string, tsep?: string): string {
|
||||
if (isNaN(_number) || _number === null || _number === undefined) return '';
|
||||
|
||||
const number = _number.toFixed(~~dec);
|
||||
tsep = typeof tsep === 'string' ? tsep : ',';
|
||||
|
||||
const parts = number.split('.');
|
||||
const fnums = parts[0];
|
||||
const decimals = parts[1] ? (dsep || '.') + parts[1] : '';
|
||||
|
||||
return fnums.replace(/(\d)(?=(?:\d{3})+$)/g, `$1${tsep}`) + decimals;
|
||||
}
|
||||
|
||||
export function pad(_str: unknown, _length: number, padStr?: string, type: 'right' | 'left' | 'both' = 'right') {
|
||||
const str = makeString(_str);
|
||||
const length = ~~_length;
|
||||
|
||||
let padlen = 0;
|
||||
|
||||
if (!padStr) padStr = ' ';
|
||||
else if (padStr.length > 1) padStr = padStr.charAt(0);
|
||||
|
||||
switch (type) {
|
||||
case 'right':
|
||||
padlen = length - str.length;
|
||||
return str + padStr.repeat(padlen);
|
||||
case 'both':
|
||||
padlen = length - str.length;
|
||||
return padStr.repeat(Math.ceil(padlen / 2)) + str + padStr.repeat(Math.floor(padlen / 2));
|
||||
default: // 'left'
|
||||
padlen = length - str.length;
|
||||
return padStr.repeat(padlen) + str;
|
||||
}
|
||||
}
|
||||
|
||||
export function lrpad(str: unknown, length: number, padStr?: string): string {
|
||||
return pad(str, length, padStr, 'both');
|
||||
}
|
||||
20
apps/meteor/lib/utils/throttledCounter.ts
Normal file
20
apps/meteor/lib/utils/throttledCounter.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { throttle } from 'underscore';
|
||||
|
||||
export function throttledCounter(fn: (counter: number) => unknown, wait: number) {
|
||||
let counter = 0;
|
||||
|
||||
const throttledFn = throttle(
|
||||
() => {
|
||||
fn(counter);
|
||||
|
||||
counter = 0;
|
||||
},
|
||||
wait,
|
||||
{ leading: false },
|
||||
);
|
||||
|
||||
return () => {
|
||||
counter++;
|
||||
throttledFn();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user