mirror of
https://github.com/sim1222/misskey.git
synced 2025-08-04 07:26:29 +09:00
3
packages/backend/src/prelude/README.md
Normal file
3
packages/backend/src/prelude/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Prelude
|
||||
このディレクトリのコードはJavaScriptの表現能力を補うためのコードです。
|
||||
Misskey固有の処理とは独立したコードの集まりですが、Misskeyのコードを読みやすくすることを目的としています。
|
138
packages/backend/src/prelude/array.ts
Normal file
138
packages/backend/src/prelude/array.ts
Normal file
@ -0,0 +1,138 @@
|
||||
import { EndoRelation, Predicate } from './relation';
|
||||
|
||||
/**
|
||||
* Count the number of elements that satisfy the predicate
|
||||
*/
|
||||
|
||||
export function countIf<T>(f: Predicate<T>, xs: T[]): number {
|
||||
return xs.filter(f).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of elements that is equal to the element
|
||||
*/
|
||||
export function count<T>(a: T, xs: T[]): number {
|
||||
return countIf(x => x === a, xs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate an array of arrays
|
||||
*/
|
||||
export function concat<T>(xss: T[][]): T[] {
|
||||
return ([] as T[]).concat(...xss);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersperse the element between the elements of the array
|
||||
* @param sep The element to be interspersed
|
||||
*/
|
||||
export function intersperse<T>(sep: T, xs: T[]): T[] {
|
||||
return concat(xs.map(x => [sep, x])).slice(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of elements that is not equal to the element
|
||||
*/
|
||||
export function erase<T>(a: T, xs: T[]): T[] {
|
||||
return xs.filter(x => x !== a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the array of all elements in the first array not contained in the second array.
|
||||
* The order of result values are determined by the first array.
|
||||
*/
|
||||
export function difference<T>(xs: T[], ys: T[]): T[] {
|
||||
return xs.filter(x => !ys.includes(x));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all but the first element from every group of equivalent elements
|
||||
*/
|
||||
export function unique<T>(xs: T[]): T[] {
|
||||
return [...new Set(xs)];
|
||||
}
|
||||
|
||||
export function sum(xs: number[]): number {
|
||||
return xs.reduce((a, b) => a + b, 0);
|
||||
}
|
||||
|
||||
export function maximum(xs: number[]): number {
|
||||
return Math.max(...xs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits an array based on the equivalence relation.
|
||||
* The concatenation of the result is equal to the argument.
|
||||
*/
|
||||
export function groupBy<T>(f: EndoRelation<T>, xs: T[]): T[][] {
|
||||
const groups = [] as T[][];
|
||||
for (const x of xs) {
|
||||
if (groups.length !== 0 && f(groups[groups.length - 1][0], x)) {
|
||||
groups[groups.length - 1].push(x);
|
||||
} else {
|
||||
groups.push([x]);
|
||||
}
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits an array based on the equivalence relation induced by the function.
|
||||
* The concatenation of the result is equal to the argument.
|
||||
*/
|
||||
export function groupOn<T, S>(f: (x: T) => S, xs: T[]): T[][] {
|
||||
return groupBy((a, b) => f(a) === f(b), xs);
|
||||
}
|
||||
|
||||
export function groupByX<T>(collections: T[], keySelector: (x: T) => string) {
|
||||
return collections.reduce((obj: Record<string, T[]>, item: T) => {
|
||||
const key = keySelector(item);
|
||||
if (!obj.hasOwnProperty(key)) {
|
||||
obj[key] = [];
|
||||
}
|
||||
|
||||
obj[key].push(item);
|
||||
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two arrays by lexicographical order
|
||||
*/
|
||||
export function lessThan(xs: number[], ys: number[]): boolean {
|
||||
for (let i = 0; i < Math.min(xs.length, ys.length); i++) {
|
||||
if (xs[i] < ys[i]) return true;
|
||||
if (xs[i] > ys[i]) return false;
|
||||
}
|
||||
return xs.length < ys.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the longest prefix of elements that satisfy the predicate
|
||||
*/
|
||||
export function takeWhile<T>(f: Predicate<T>, xs: T[]): T[] {
|
||||
const ys = [];
|
||||
for (const x of xs) {
|
||||
if (f(x)) {
|
||||
ys.push(x);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ys;
|
||||
}
|
||||
|
||||
export function cumulativeSum(xs: number[]): number[] {
|
||||
const ys = Array.from(xs); // deep copy
|
||||
for (let i = 1; i < ys.length; i++) ys[i] += ys[i - 1];
|
||||
return ys;
|
||||
}
|
||||
|
||||
export function toArray<T>(x: T | T[] | undefined): T[] {
|
||||
return Array.isArray(x) ? x : x != null ? [x] : [];
|
||||
}
|
||||
|
||||
export function toSingle<T>(x: T | T[] | undefined): T | undefined {
|
||||
return Array.isArray(x) ? x[0] : x;
|
||||
}
|
23
packages/backend/src/prelude/await-all.ts
Normal file
23
packages/backend/src/prelude/await-all.ts
Normal file
@ -0,0 +1,23 @@
|
||||
type Await<T> = T extends Promise<infer U> ? U : T;
|
||||
|
||||
type AwaitAll<T> = {
|
||||
[P in keyof T]: Await<T[P]>;
|
||||
};
|
||||
|
||||
export async function awaitAll<T>(obj: T): Promise<AwaitAll<T>> {
|
||||
const target = {} as any;
|
||||
const keys = Object.keys(obj);
|
||||
const values = Object.values(obj);
|
||||
|
||||
const resolvedValues = await Promise.all(values.map(value =>
|
||||
(!value || !value.constructor || value.constructor.name !== 'Object')
|
||||
? value
|
||||
: awaitAll(value)
|
||||
));
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
target[keys[i]] = resolvedValues[i];
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
3
packages/backend/src/prelude/math.ts
Normal file
3
packages/backend/src/prelude/math.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function gcd(a: number, b: number): number {
|
||||
return b === 0 ? a : gcd(b, a % b);
|
||||
}
|
20
packages/backend/src/prelude/maybe.ts
Normal file
20
packages/backend/src/prelude/maybe.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export interface IMaybe<T> {
|
||||
isJust(): this is IJust<T>;
|
||||
}
|
||||
|
||||
export interface IJust<T> extends IMaybe<T> {
|
||||
get(): T;
|
||||
}
|
||||
|
||||
export function just<T>(value: T): IJust<T> {
|
||||
return {
|
||||
isJust: () => true,
|
||||
get: () => value
|
||||
};
|
||||
}
|
||||
|
||||
export function nothing<T>(): IMaybe<T> {
|
||||
return {
|
||||
isJust: () => false,
|
||||
};
|
||||
}
|
5
packages/backend/src/prelude/relation.ts
Normal file
5
packages/backend/src/prelude/relation.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export type Predicate<T> = (a: T) => boolean;
|
||||
|
||||
export type Relation<T, U> = (a: T, b: U) => boolean;
|
||||
|
||||
export type EndoRelation<T> = Relation<T, T>;
|
15
packages/backend/src/prelude/string.ts
Normal file
15
packages/backend/src/prelude/string.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export function concat(xs: string[]): string {
|
||||
return xs.join('');
|
||||
}
|
||||
|
||||
export function capitalize(s: string): string {
|
||||
return toUpperCase(s.charAt(0)) + toLowerCase(s.slice(1));
|
||||
}
|
||||
|
||||
export function toUpperCase(s: string): string {
|
||||
return s.toUpperCase();
|
||||
}
|
||||
|
||||
export function toLowerCase(s: string): string {
|
||||
return s.toLowerCase();
|
||||
}
|
1
packages/backend/src/prelude/symbol.ts
Normal file
1
packages/backend/src/prelude/symbol.ts
Normal file
@ -0,0 +1 @@
|
||||
export const fallback = Symbol('fallback');
|
39
packages/backend/src/prelude/time.ts
Normal file
39
packages/backend/src/prelude/time.ts
Normal file
@ -0,0 +1,39 @@
|
||||
const dateTimeIntervals = {
|
||||
'day': 86400000,
|
||||
'hour': 3600000,
|
||||
'ms': 1,
|
||||
};
|
||||
|
||||
export function dateUTC(time: number[]): Date {
|
||||
const d = time.length === 2 ? Date.UTC(time[0], time[1])
|
||||
: time.length === 3 ? Date.UTC(time[0], time[1], time[2])
|
||||
: time.length === 4 ? Date.UTC(time[0], time[1], time[2], time[3])
|
||||
: time.length === 5 ? Date.UTC(time[0], time[1], time[2], time[3], time[4])
|
||||
: time.length === 6 ? Date.UTC(time[0], time[1], time[2], time[3], time[4], time[5])
|
||||
: time.length === 7 ? Date.UTC(time[0], time[1], time[2], time[3], time[4], time[5], time[6])
|
||||
: null;
|
||||
|
||||
if (!d) throw 'wrong number of arguments';
|
||||
|
||||
return new Date(d);
|
||||
}
|
||||
|
||||
export function isTimeSame(a: Date, b: Date): boolean {
|
||||
return a.getTime() === b.getTime();
|
||||
}
|
||||
|
||||
export function isTimeBefore(a: Date, b: Date): boolean {
|
||||
return (a.getTime() - b.getTime()) < 0;
|
||||
}
|
||||
|
||||
export function isTimeAfter(a: Date, b: Date): boolean {
|
||||
return (a.getTime() - b.getTime()) > 0;
|
||||
}
|
||||
|
||||
export function addTime(x: Date, value: number, span: keyof typeof dateTimeIntervals = 'ms'): Date {
|
||||
return new Date(x.getTime() + (value * dateTimeIntervals[span]));
|
||||
}
|
||||
|
||||
export function subtractTime(x: Date, value: number, span: keyof typeof dateTimeIntervals = 'ms'): Date {
|
||||
return new Date(x.getTime() - (value * dateTimeIntervals[span]));
|
||||
}
|
13
packages/backend/src/prelude/url.ts
Normal file
13
packages/backend/src/prelude/url.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export function query(obj: {}): string {
|
||||
const params = Object.entries(obj)
|
||||
.filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined)
|
||||
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);
|
||||
|
||||
return Object.entries(params)
|
||||
.map((e) => `${e[0]}=${encodeURIComponent(e[1])}`)
|
||||
.join('&');
|
||||
}
|
||||
|
||||
export function appendQuery(url: string, query: string): string {
|
||||
return `${url}${/\?/.test(url) ? url.endsWith('?') ? '' : '&' : '?'}${query}`;
|
||||
}
|
41
packages/backend/src/prelude/xml.ts
Normal file
41
packages/backend/src/prelude/xml.ts
Normal file
@ -0,0 +1,41 @@
|
||||
const map: Record<string, string> = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
'\'': '''
|
||||
};
|
||||
|
||||
const beginingOfCDATA = '<![CDATA[';
|
||||
const endOfCDATA = ']]>';
|
||||
|
||||
export function escapeValue(x: string): string {
|
||||
let insideOfCDATA = false;
|
||||
let builder = '';
|
||||
for (
|
||||
let i = 0;
|
||||
i < x.length;
|
||||
) {
|
||||
if (insideOfCDATA) {
|
||||
if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
|
||||
insideOfCDATA = true;
|
||||
i += beginingOfCDATA.length;
|
||||
} else {
|
||||
builder += x[i++];
|
||||
}
|
||||
} else {
|
||||
if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
|
||||
insideOfCDATA = false;
|
||||
i += endOfCDATA.length;
|
||||
} else {
|
||||
const b = x[i++];
|
||||
builder += map[b] || b;
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
export function escapeAttribute(x: string): string {
|
||||
return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
|
||||
}
|
Reference in New Issue
Block a user