Set up typescript-rtti (WIP)
This commit is contained in:
parent
cc1e66c054
commit
35a8c62ff2
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,7 +2,8 @@
|
|||||||
*~
|
*~
|
||||||
# Typescript
|
# Typescript
|
||||||
# emitted code
|
# emitted code
|
||||||
obj
|
build/*
|
||||||
|
!build/package.json
|
||||||
|
|
||||||
# ---> Node
|
# ---> Node
|
||||||
# Logs
|
# Logs
|
||||||
|
@ -5,6 +5,6 @@ A final (?) prototype for a refactor of mathjs, culminating the picomath, pocoma
|
|||||||
To build and run the prototype, run:
|
To build and run the prototype, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
npx tsc
|
pnpm install
|
||||||
node obj
|
pnpm build-and-run
|
||||||
```
|
```
|
||||||
|
3
build/package.json
Normal file
3
build/package.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept',
|
description: 'A hopeful final typescipt-pragmatic mathjs proof-of-concept',
|
||||||
main: 'index.ts',
|
main: 'index.ts',
|
||||||
scripts: {
|
scripts: {
|
||||||
|
'build-and-run': 'ttsc -b && node build',
|
||||||
test: 'echo "Error: no test specified" && exit 1',
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
},
|
},
|
||||||
keywords: [
|
keywords: [
|
||||||
@ -17,7 +18,15 @@
|
|||||||
type: 'git',
|
type: 'git',
|
||||||
url: 'https://code.studioinfinity.org/glen/typocomath.git',
|
url: 'https://code.studioinfinity.org/glen/typocomath.git',
|
||||||
},
|
},
|
||||||
|
dependencies: {
|
||||||
|
'reflect-metadata': '0.1.13',
|
||||||
|
'typescript-rtti': '0.8.2',
|
||||||
|
},
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
typescript: '^4.9.3',
|
'@types/node': '18.11.18',
|
||||||
|
'ts-node': '10.9.1',
|
||||||
|
ttypescript: '1.5.15',
|
||||||
|
typescript: '4.8.4',
|
||||||
|
'typescript-rtti': '0.8.2',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
194
pnpm-lock.yaml
194
pnpm-lock.yaml
@ -1,15 +1,199 @@
|
|||||||
lockfileVersion: 5.4
|
lockfileVersion: 5.4
|
||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
typescript: ^4.9.3
|
'@types/node': 18.11.18
|
||||||
|
reflect-metadata: 0.1.13
|
||||||
|
ts-node: 10.9.1
|
||||||
|
ttypescript: 1.5.15
|
||||||
|
typescript: 4.8.4
|
||||||
|
typescript-rtti: 0.8.2
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
reflect-metadata: 0.1.13
|
||||||
|
typescript-rtti: 0.8.2_tjkpoysld524rvy7hjviaz6rje
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
typescript: 4.9.3
|
'@types/node': 18.11.18
|
||||||
|
ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm
|
||||||
|
ttypescript: 1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka
|
||||||
|
typescript: 4.8.4
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
/typescript/4.9.3:
|
/@cspotcode/source-map-support/0.8.1:
|
||||||
resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==}
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
engines: {node: '>=4.2.0'}
|
engines: {node: '>=12'}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/resolve-uri/3.1.0:
|
||||||
|
resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/sourcemap-codec/1.4.14:
|
||||||
|
resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@jridgewell/trace-mapping/0.3.9:
|
||||||
|
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/resolve-uri': 3.1.0
|
||||||
|
'@jridgewell/sourcemap-codec': 1.4.14
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@tsconfig/node10/1.0.9:
|
||||||
|
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@tsconfig/node12/1.0.11:
|
||||||
|
resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@tsconfig/node14/1.0.3:
|
||||||
|
resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@tsconfig/node16/1.0.3:
|
||||||
|
resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/@types/node/18.11.18:
|
||||||
|
resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/acorn-walk/8.2.0:
|
||||||
|
resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/acorn/8.8.2:
|
||||||
|
resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==}
|
||||||
|
engines: {node: '>=0.4.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/arg/4.1.3:
|
||||||
|
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/create-require/1.1.1:
|
||||||
|
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/diff/4.0.2:
|
||||||
|
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
|
||||||
|
engines: {node: '>=0.3.1'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/function-bind/1.1.1:
|
||||||
|
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/has/1.0.3:
|
||||||
|
resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
|
||||||
|
engines: {node: '>= 0.4.0'}
|
||||||
|
dependencies:
|
||||||
|
function-bind: 1.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/is-core-module/2.11.0:
|
||||||
|
resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
|
||||||
|
dependencies:
|
||||||
|
has: 1.0.3
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/make-error/1.3.6:
|
||||||
|
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/path-parse/1.0.7:
|
||||||
|
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/reflect-metadata/0.1.13:
|
||||||
|
resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/resolve/1.22.1:
|
||||||
|
resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
is-core-module: 2.11.0
|
||||||
|
path-parse: 1.0.7
|
||||||
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/supports-preserve-symlinks-flag/1.0.0:
|
||||||
|
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||||
|
engines: {node: '>= 0.4'}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/ts-node/10.9.1_vqcafhj4xvr2nzknlrdklk55zm:
|
||||||
|
resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@swc/core': '>=1.2.50'
|
||||||
|
'@swc/wasm': '>=1.2.50'
|
||||||
|
'@types/node': '*'
|
||||||
|
typescript: '>=2.7'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
'@swc/wasm':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@cspotcode/source-map-support': 0.8.1
|
||||||
|
'@tsconfig/node10': 1.0.9
|
||||||
|
'@tsconfig/node12': 1.0.11
|
||||||
|
'@tsconfig/node14': 1.0.3
|
||||||
|
'@tsconfig/node16': 1.0.3
|
||||||
|
'@types/node': 18.11.18
|
||||||
|
acorn: 8.8.2
|
||||||
|
acorn-walk: 8.2.0
|
||||||
|
arg: 4.1.3
|
||||||
|
create-require: 1.1.1
|
||||||
|
diff: 4.0.2
|
||||||
|
make-error: 1.3.6
|
||||||
|
typescript: 4.8.4
|
||||||
|
v8-compile-cache-lib: 3.0.1
|
||||||
|
yn: 3.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/ttypescript/1.5.15_mwhvu7sfp6vq5ryuwb6hlbjfka:
|
||||||
|
resolution: {integrity: sha512-48ykDNHzFnPMnv4hYX1P8Q84TvCZyL1QlFxeuxsuZ48X2+ameBgPenvmCkHJtoOSxpoWTWi8NcgNrRnVDOmfSg==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
ts-node: '>=8.0.2'
|
||||||
|
typescript: '>=3.2.2'
|
||||||
|
dependencies:
|
||||||
|
resolve: 1.22.1
|
||||||
|
ts-node: 10.9.1_vqcafhj4xvr2nzknlrdklk55zm
|
||||||
|
typescript: 4.8.4
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/typescript-rtti/0.8.2_tjkpoysld524rvy7hjviaz6rje:
|
||||||
|
resolution: {integrity: sha512-MZUHMX+Up1+7dYaAcOYSTxKc4PNLNXhuXYBkIzEKvQvxJ6xp7wcjTtiIYmB9+RJxRH5/9phZLqKTMpy20aQ4Aw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
reflect-metadata: ^0.1.13
|
||||||
|
typescript: '4.6'
|
||||||
|
dependencies:
|
||||||
|
reflect-metadata: 0.1.13
|
||||||
|
typescript: 4.8.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/typescript/4.8.4:
|
||||||
|
resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==}
|
||||||
|
engines: {node: '>=4.2.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
/v8-compile-cache-lib/3.0.1:
|
||||||
|
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/yn/3.1.1:
|
||||||
|
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
dev: true
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import "reflect-metadata"
|
||||||
|
import { reflect, type CallSite } from 'typescript-rtti'
|
||||||
|
|
||||||
/* A Dispatcher is a collection of operations that do run-time
|
/* A Dispatcher is a collection of operations that do run-time
|
||||||
* dispatch on the types of their arguments. Thus, every individual
|
* dispatch on the types of their arguments. Thus, every individual
|
||||||
* method is like a typed-function (from the library by that name),
|
* method is like a typed-function (from the library by that name),
|
||||||
@ -46,6 +49,10 @@ export class Dispatcher {
|
|||||||
// that's really possible, though.
|
// that's really possible, though.
|
||||||
) {
|
) {
|
||||||
console.log('Pretending to install', name, signature, '=>', returns)
|
console.log('Pretending to install', name, signature, '=>', returns)
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
console.log(name, 'signature', reflect(signature))
|
||||||
|
console.log(name, 'dependencies', reflect(dependencies))
|
||||||
//TODO: implement me
|
//TODO: implement me
|
||||||
}
|
}
|
||||||
installType(name: TypeName, typespec: TypeSpecification) {
|
installType(name: TypeName, typespec: TypeSpecification) {
|
||||||
|
52
src/index.ts
52
src/index.ts
@ -1,11 +1,57 @@
|
|||||||
import {Dispatcher} from './core/Dispatcher.js'
|
import {Dispatcher} from './core/Dispatcher.js'
|
||||||
import * as Specifications from './all.js'
|
import * as Specifications from './all.js'
|
||||||
|
|
||||||
export default new Dispatcher(Specifications)
|
|
||||||
|
|
||||||
import {Complex} from './Complex/type.js'
|
import {Complex} from './Complex/type.js'
|
||||||
import {absquare as absquare_complex} from './Complex/arithmetic.js'
|
import {absquare as absquare_complex} from './Complex/arithmetic.js'
|
||||||
|
|
||||||
|
import 'reflect-metadata'
|
||||||
|
import { ReflectedFunctionParameter, ReflectedObjectRef, reflect } from 'typescript-rtti'
|
||||||
|
import { square } from './generic/arithmetic.js'
|
||||||
|
|
||||||
|
// verify that typescript-rtti works (just as experiment)
|
||||||
|
const add = (a: number, b: number): number => a + b
|
||||||
|
console.log('reflect add')
|
||||||
|
console.log('parameterNames', reflect(add).parameterNames)
|
||||||
|
console.log('parameterTypes', reflect(add).parameterTypes.map(type => type.toString()))
|
||||||
|
console.log('returnType', reflect(add).returnType.toString())
|
||||||
|
console.log()
|
||||||
|
// output:
|
||||||
|
// reflect function add
|
||||||
|
// parameterNames [ 'a', 'b' ]
|
||||||
|
// parameterTypes [ 'class Number', 'class Number' ]
|
||||||
|
// returnType class Number
|
||||||
|
|
||||||
|
// try out a very simple case (just as experiment)
|
||||||
|
function createSquare(deps: {
|
||||||
|
multiply: (a: number, b: number) => number,
|
||||||
|
subtract: (a: number, b: number) => number
|
||||||
|
}) {
|
||||||
|
return (a: number) => deps.multiply(a, a)
|
||||||
|
}
|
||||||
|
console.log('reflect createSquare')
|
||||||
|
console.log('parameter names', reflect(createSquare).parameters.map(parameter => parameter.name))
|
||||||
|
console.log('parameter[0]', (reflect(createSquare).parameters[0] as ReflectedFunctionParameter))
|
||||||
|
// @ts-ignore
|
||||||
|
console.log('parameterTypes[0]', (reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m)
|
||||||
|
console.log('parameterTypes[0][0]',
|
||||||
|
// @ts-ignore
|
||||||
|
(reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0].n,
|
||||||
|
// @ts-ignore
|
||||||
|
(reflect(createSquare).parameterTypes[0] as ReflectedObjectRef)._ref.m[0]
|
||||||
|
)
|
||||||
|
// @ts-ignore
|
||||||
|
console.log('parameters[0]', reflect(createSquare).parameters[0])
|
||||||
|
// FIXME: where to find the information of the types of the dependencies multiply and subtract?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: get all types out of Specifications
|
||||||
|
// console.log('Specifications', reflect(Specifications)) // Throws errors
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: import all specificiations (turned off for debugging purposes)
|
||||||
|
// export default new Dispatcher(Specifications)
|
||||||
|
|
||||||
|
|
||||||
const mockRealAdd = (a: number, b: number) => a+b
|
const mockRealAdd = (a: number, b: number) => a+b
|
||||||
const mockComplexAbsquare = (z: Complex<number>) => z.re*z.re + z.im*z.im
|
const mockComplexAbsquare = (z: Complex<number>) => z.re*z.re + z.im*z.im
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ export const absquare: Signature<'absquare', number> = a => a * a
|
|||||||
export const reciprocal: Signature<'reciprocal', number> = a => 1 / a
|
export const reciprocal: Signature<'reciprocal', number> = a => 1 / a
|
||||||
export const divide: Signature<'divide', number> = (a, b) => a / b
|
export const divide: Signature<'divide', number> = (a, b) => a / b
|
||||||
|
|
||||||
const basicSqrt = a => isNaN(a) ? NaN : Math.sqrt(a)
|
const basicSqrt = (a: number) => isNaN(a) ? NaN : Math.sqrt(a)
|
||||||
export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt
|
export const conservativeSqrt: Signature<'conservativeSqrt', number> = basicSqrt
|
||||||
|
|
||||||
export const sqrt =
|
export const sqrt =
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"outDir": "./obj"
|
"outDir": "./build",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowJs": false,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"transform": "typescript-rtti/dist/transformer"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user